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