[pypy-commit] pypy py3.3: Change classes .__dict__ to be a distinct "mappingproxy" object.

amauryfa noreply at buildbot.pypy.org
Wed Oct 29 23:54:38 CET 2014


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.3
Changeset: r74292:759a9ee11989
Date: 2014-10-29 23:52 +0100
http://bitbucket.org/pypy/pypy/changeset/759a9ee11989/

Log:	Change classes .__dict__ to be a distinct "mappingproxy" object.
	It's a subclass of dict and behaves like before.

	But its type can be used to wrap any mapping, and acts as a read-
	only proxy to this mapping. For example,
	type(int.__dict__)(OrderedDict(...)) is a read-only ordered dict,
	and inspect.signature() uses it this way.

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -453,11 +453,8 @@
         self._value = value
         self.setup(w_type)
 
-    def get_w_value(self, space):
-        w_value = self._w_value
-        if w_value is None:
-            self._w_value = w_value = space.wrap(self._value)
-        return w_value
+    def _compute_value(self, space):
+        return self._value.decode('utf-8')
 
 @specialize.memo()
 def get_operr_class(valuefmt):
diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py
--- a/pypy/objspace/std/dictmultiobject.py
+++ b/pypy/objspace/std/dictmultiobject.py
@@ -626,7 +626,10 @@
     next_item = _new_next('item')
 
 
-def create_iterator_classes(dictimpl, override_next_item=None):
+def create_iterator_classes(dictimpl,
+                            override_next_key=None,
+                            override_next_value=None,
+                            override_next_item=None):
     if not hasattr(dictimpl, 'wrapkey'):
         wrapkey = lambda space, key: key
     else:
@@ -647,22 +650,28 @@
             self.iterator = strategy.getiterkeys(impl)
             BaseIteratorImplementation.__init__(self, space, strategy, impl)
 
-        def next_key_entry(self):
-            for key in self.iterator:
-                return wrapkey(self.space, key)
-            else:
-                return None
+        if override_next_key is not None:
+            next_key_entry = override_next_key
+        else:
+            def next_key_entry(self):
+                for key in self.iterator:
+                    return wrapkey(self.space, key)
+                else:
+                    return None
 
     class IterClassValues(BaseValueIterator):
         def __init__(self, space, strategy, impl):
             self.iterator = strategy.getitervalues(impl)
             BaseIteratorImplementation.__init__(self, space, strategy, impl)
 
-        def next_value_entry(self):
-            for value in self.iterator:
-                return wrapvalue(self.space, value)
-            else:
-                return None
+        if override_next_value is not None:
+            next_value_entry = override_next_value
+        else:
+            def next_value_entry(self):
+                for value in self.iterator:
+                    return wrapvalue(self.space, value)
+                else:
+                    return None
 
     class IterClassItems(BaseItemIterator):
         def __init__(self, space, strategy, impl):
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
@@ -1,18 +1,46 @@
-from pypy.objspace.std.dictmultiobject import DictStrategy, create_iterator_classes
+from pypy.objspace.std.dictmultiobject import (
+    W_DictMultiObject, DictStrategy, create_iterator_classes)
 from pypy.objspace.std.typeobject import unwrap_cell
+from pypy.objspace.std.stdtypedef import StdTypeDef
 from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.gateway import interp2app
 
 from rpython.rlib import rerased
 
 
+class W_DictProxyObject(W_DictMultiObject):
+    @staticmethod
+    def descr_new(space, w_type, w_mapping):
+        strategy = space.fromcache(MappingProxyStrategy)
+        storage = strategy.erase(w_mapping)
+        w_obj = space.allocate_instance(W_DictProxyObject, w_type)
+        W_DictProxyObject.__init__(w_obj, space, strategy, storage)
+        return w_obj
+
+    def descr_init(self, space, __args__):
+        pass
+
+    def descr_repr(self, space):
+        return space.wrap(u"mappingproxy(%s)" % (
+            space.unicode_w(W_DictMultiObject.descr_repr(self, space))))
+
+W_DictProxyObject.typedef = StdTypeDef(
+    "mappingproxy", W_DictMultiObject.typedef,
+    __new__ = interp2app(W_DictProxyObject.descr_new),
+    __init__ = interp2app(W_DictProxyObject.descr_init),
+    __repr__ = interp2app(W_DictProxyObject.descr_repr),
+)
+
+
 class DictProxyStrategy(DictStrategy):
+    """Exposes a W_TypeObject.dict_w at app-level.
+
+    Uses getdictvalue() and setdictvalue() to access items.
+    """
     erase, unerase = rerased.new_erasing_pair("dictproxy")
     erase = staticmethod(erase)
     unerase = staticmethod(unerase)
 
-    def __init__(w_self, space):
-        DictStrategy.__init__(w_self, space)
-
     def getitem(self, w_dict, w_key):
         space = self.space
         w_lookup_type = space.type(w_key)
@@ -108,3 +136,62 @@
     return space.wrap(key.decode('utf-8'))
 
 create_iterator_classes(DictProxyStrategy)
+
+
+class MappingProxyStrategy(DictStrategy):
+    """Wraps an applevel mapping in a read-only dictionary."""
+    erase, unerase = rerased.new_erasing_pair("mappingproxy")
+    erase = staticmethod(erase)
+    unerase = staticmethod(unerase)
+
+    def getitem(self, w_dict, w_key):
+        return self.space.getitem(self.unerase(w_dict.dstorage), w_key)
+
+    def setitem(self, w_dict, w_key, w_value):
+        raise oefmt(self.space.w_TypeError,
+                    "'%T' object does not support item assignment", w_dict)
+
+    def delitem(self, w_dict, w_key):
+        raise oefmt(self.space.w_TypeError,
+                    "'%T' object does not support item deletion", w_dict)
+
+    def length(self, w_dict):
+        return self.space.len_w(self.unerase(w_dict.dstorage))
+
+    def getiterkeys(self, w_dict):
+        return self.space.iter(
+            self.space.call_method(self.unerase(w_dict.dstorage), "keys"))
+
+    def getitervalues(self, w_dict):
+        return self.space.iter(
+            self.space.call_method(self.unerase(w_dict.dstorage), "values"))
+
+    def getiteritems(self, w_dict):
+        return self.space.iter(
+            self.space.call_method(self.unerase(w_dict.dstorage), "items"))
+
+    @staticmethod
+    def override_next_key(iterkeys):
+        w_keys = iterkeys.iterator
+        return iterkeys.space.next(w_keys)
+
+    @staticmethod
+    def override_next_value(itervalues):
+        w_values = itervalues.iterator
+        return itervalues.space.next(w_values)
+
+    @staticmethod
+    def override_next_item(iteritems):
+        w_items = iteritems.iterator
+        w_item = iteritems.space.next(w_items)
+        w_key, w_value = iteritems.space.unpackiterable(w_item, 2)
+        return w_key, w_value
+
+    def clear(self, w_dict):
+        raise oefmt(self.space.w_AttributeError, "clear")
+
+create_iterator_classes(
+    MappingProxyStrategy,
+    override_next_key=MappingProxyStrategy.override_next_key,
+    override_next_value=MappingProxyStrategy.override_next_value,
+    override_next_item=MappingProxyStrategy.override_next_item)
diff --git a/pypy/objspace/std/test/test_dictproxy.py b/pypy/objspace/std/test/test_dictproxy.py
--- a/pypy/objspace/std/test/test_dictproxy.py
+++ b/pypy/objspace/std/test/test_dictproxy.py
@@ -53,12 +53,26 @@
         s1 = repr(a.__dict__)
         s2 = str(a.__dict__)
         assert s1 == s2
-        assert s1.startswith('{') and s1.endswith('}')
+        assert s1.startswith('mappingproxy({') and s1.endswith('})')
 
     def test_immutable_dict_on_builtin_type(self):
         raises(TypeError, "int.__dict__['a'] = 1")
-        raises(TypeError, int.__dict__.popitem)
-        raises(TypeError, int.__dict__.clear)
+        raises((AttributeError, TypeError), "int.__dict__.popitem()")
+        raises((AttributeError, TypeError), "int.__dict__.clear()")
+
+    def test_mappingproxy(self):
+        dictproxy = type(int.__dict__)
+        assert dictproxy is not dict
+        assert dictproxy.__name__ == 'mappingproxy'
+        raises(TypeError, dictproxy)
+        mapping = dict(a=1, b=2, c=3)
+        proxy = dictproxy(mapping)
+        assert proxy['a'] == 1
+        assert repr(proxy) == 'mappingproxy(%r)' % mapping
+        assert proxy.keys() == mapping.keys()
+        raises(TypeError, "proxy['a'] = 4")
+        raises(TypeError, "del proxy['a']")
+        raises(AttributeError, "proxy.clear()")
 
 class AppTestUserObjectMethodCache(AppTestUserObject):
     spaceconfig = {"objspace.std.withmethodcachecounter": True}
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
@@ -438,12 +438,12 @@
 
     def getdict(w_self, space): # returning a dict-proxy!
         from pypy.objspace.std.dictproxyobject import DictProxyStrategy
-        from pypy.objspace.std.dictmultiobject import W_DictMultiObject
+        from pypy.objspace.std.dictproxyobject import W_DictProxyObject
         if w_self.lazyloaders:
             w_self._cleanup_()    # force un-lazification
         strategy = space.fromcache(DictProxyStrategy)
         storage = strategy.erase(w_self)
-        return W_DictMultiObject(space, strategy, storage)
+        return W_DictProxyObject(space, strategy, storage)
 
     def unwrap(w_self, space):
         from pypy.objspace.std.model import UnwrapError


More information about the pypy-commit mailing list