Unfortunately this commit has some bad effects.  Going through an iterator in popitem() will result in O(N**2) behavior for repeated calls.  If you look at the r_dict implementation of popitem() you can see the fix there.<div>
<br></div><div>Alex<br><br><div class="gmail_quote">On Mon, Feb 20, 2012 at 6:18 AM, cfbolz <span dir="ltr">&lt;<a href="mailto:noreply@buildbot.pypy.org">noreply@buildbot.pypy.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Author: Carl Friedrich Bolz &lt;<a href="mailto:cfbolz@gmx.de">cfbolz@gmx.de</a>&gt;<br>
Branch:<br>
Changeset: r52670:4b254e123047<br>
Date: 2012-02-20 12:17 +0100<br>
<a href="http://bitbucket.org/pypy/pypy/changeset/4b254e123047/" target="_blank">http://bitbucket.org/pypy/pypy/changeset/4b254e123047/</a><br>
<br>
Log:    issue1059 testing<br>
<br>
        make the .__dict__.clear method of builtin types raise an error. Fix<br>
        popitem on dict proxies (builtin types raise an error, normal types<br>
        work normally).<br>
<br>
diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py<br>
--- a/pypy/objspace/std/dictmultiobject.py<br>
+++ b/pypy/objspace/std/dictmultiobject.py<br>
@@ -142,6 +142,13 @@<br>
             else:<br>
                 return result<br>
<br>
+    def popitem(self, w_dict):<br>
+        space = self.space<br>
+        iterator = self.iter(w_dict)<br>
+        w_key, w_value = iterator.next()<br>
+        self.delitem(w_dict, w_key)<br>
+        return (w_key, w_value)<br>
+<br>
     def clear(self, w_dict):<br>
         strategy = self.space.fromcache(EmptyDictStrategy)<br>
         storage = strategy.get_empty_storage()<br>
diff --git a/pypy/objspace/std/dictproxyobject.py b/pypy/objspace/std/dictproxyobject.py<br>
--- a/pypy/objspace/std/dictproxyobject.py<br>
+++ b/pypy/objspace/std/dictproxyobject.py<br>
@@ -3,7 +3,7 @@<br>
 from pypy.objspace.std.dictmultiobject import W_DictMultiObject, IteratorImplementation<br>
 from pypy.objspace.std.dictmultiobject import DictStrategy<br>
 from pypy.objspace.std.typeobject import unwrap_cell<br>
-from pypy.interpreter.error import OperationError<br>
+from pypy.interpreter.error import OperationError, operationerrfmt<br>
<br>
 from pypy.rlib import rerased<br>
<br>
@@ -44,7 +44,8 @@<br>
                 raise<br>
             if not w_type.is_cpytype():<br>
                 raise<br>
-            # xxx obscure workaround: allow cpyext to write to type-&gt;tp_dict.<br>
+            # xxx obscure workaround: allow cpyext to write to type-&gt;tp_dict<br>
+            # xxx even in the case of a builtin type.<br>
             # xxx like CPython, we assume that this is only done early after<br>
             # xxx the type is created, and we don&#39;t invalidate any cache.<br>
             w_type.dict_w[key] = w_value<br>
@@ -86,8 +87,14 @@<br>
                     for (key, w_value) in self.unerase(w_dict.dstorage).dict_w.iteritems()]<br>
<br>
     def clear(self, w_dict):<br>
-        self.unerase(w_dict.dstorage).dict_w.clear()<br>
-        self.unerase(w_dict.dstorage).mutated(None)<br>
+        space = self.space<br>
+        w_type = self.unerase(w_dict.dstorage)<br>
+        if (not space.config.objspace.std.mutable_builtintypes<br>
+                and not w_type.is_heaptype()):<br>
+            msg = &quot;can&#39;t clear dictionary of type &#39;%s&#39;&quot;<br>
+            raise operationerrfmt(space.w_TypeError, msg, <a href="http://w_type.name" target="_blank">w_type.name</a>)<br>
+        w_type.dict_w.clear()<br>
+        w_type.mutated(None)<br>
<br>
 class DictProxyIteratorImplementation(IteratorImplementation):<br>
     def __init__(self, space, strategy, dictimplementation):<br>
diff --git a/pypy/objspace/std/test/test_dictproxy.py b/pypy/objspace/std/test/test_dictproxy.py<br>
--- a/pypy/objspace/std/test/test_dictproxy.py<br>
+++ b/pypy/objspace/std/test/test_dictproxy.py<br>
@@ -22,6 +22,9 @@<br>
         assert NotEmpty.string == 1<br>
         raises(TypeError, &#39;NotEmpty.__dict__.setdefault(15, 1)&#39;)<br>
<br>
+        key, value = NotEmpty.__dict__.popitem()<br>
+        assert (key == &#39;a&#39; and value == 1) or (key == &#39;b&#39; and value == 4)<br>
+<br>
     def test_dictproxyeq(self):<br>
         class a(object):<br>
             pass<br>
@@ -43,6 +46,11 @@<br>
         assert s1 == s2<br>
         assert s1.startswith(&#39;{&#39;) and s1.endswith(&#39;}&#39;)<br>
<br>
+    def test_immutable_dict_on_builtin_type(self):<br>
+        raises(TypeError, &quot;int.__dict__[&#39;a&#39;] = 1&quot;)<br>
+        raises(TypeError, int.__dict__.popitem)<br>
+        raises(TypeError, int.__dict__.clear)<br>
+<br>
 class AppTestUserObjectMethodCache(AppTestUserObject):<br>
     def setup_class(cls):<br>
         cls.space = gettestobjspace(<br>
diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py<br>
--- a/pypy/objspace/std/test/test_typeobject.py<br>
+++ b/pypy/objspace/std/test/test_typeobject.py<br>
@@ -993,7 +993,9 @@<br>
         raises(TypeError, setattr, list, &#39;append&#39;, 42)<br>
         raises(TypeError, setattr, list, &#39;foobar&#39;, 42)<br>
         raises(TypeError, delattr, dict, &#39;keys&#39;)<br>
-<br>
+        raises(TypeError, &#39;int.__dict__[&quot;a&quot;] = 1&#39;)<br>
+        raises(TypeError, &#39;int.__dict__.clear()&#39;)<br>
+<br>
     def test_nontype_in_mro(self):<br>
         class OldStyle:<br>
             pass<br>
_______________________________________________<br>
pypy-commit mailing list<br>
<a href="mailto:pypy-commit@python.org">pypy-commit@python.org</a><br>
<a href="http://mail.python.org/mailman/listinfo/pypy-commit" target="_blank">http://mail.python.org/mailman/listinfo/pypy-commit</a><br>
</blockquote></div><br><br clear="all"><div><br></div>-- <br>&quot;I disapprove of what you say, but I will defend to the death your right to say it.&quot; -- Evelyn Beatrice Hall (summarizing Voltaire)<br>&quot;The people&#39;s good is the highest law.&quot; -- Cicero<br>
<br>
</div>