Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3k Changeset: r86091:25c8dc3d6d4c Date: 2016-08-08 18:48 +0100 http://bitbucket.org/pypy/pypy/changeset/25c8dc3d6d4c/
Log: Merged in mappingproxy (pull request #469) Fix the mappingproxy type to behave as in CPython and consolidate its duplicate implementations in cpyext and objspace into a single one. Note that app-level cls.__dict__ and C-level cls->tp_dict now return different objects with the former being an opaque (at app- level) wrapper around the latter. diff --git a/pypy/module/cppyy/pythonify.py b/pypy/module/cppyy/pythonify.py --- a/pypy/module/cppyy/pythonify.py +++ b/pypy/module/cppyy/pythonify.py @@ -175,7 +175,7 @@ "__new__" : make_new(class_name), } pycppclass = metacpp(class_name, _drop_cycles(bases), d) - + # cache result early so that the class methods can find the class itself setattr(scope, final_class_name, pycppclass) @@ -192,13 +192,10 @@ for dm_name in cppclass.get_datamember_names(): cppdm = cppclass.get_datamember(dm_name) - # here, setattr() can not be used, because a data member can shadow one in - # its base class, resulting in the __set__() of its base class being called - # by setattr(); so, store directly on the dictionary - pycppclass.__dict__[dm_name] = cppdm + setattr(pycppclass, dm_name, cppdm) import cppyy if cppyy._is_static(cppdm): # TODO: make this a method of cppdm - metacpp.__dict__[dm_name] = cppdm + setattr(metacpp, dm_name, cppdm) # the call to register will add back-end specific pythonizations and thus # needs to run first, so that the generic pythonizations can use them @@ -413,7 +410,7 @@ lib = cppyy._load_dictionary(name) _loaded_dictionaries[name] = lib return lib - + def _init_pythonify(): # cppyy should not be loaded at the module level, as that will trigger a # call to space.getbuiltinmodule(), which will cause cppyy to be loaded diff --git a/pypy/module/cpyext/dictproxyobject.py b/pypy/module/cpyext/dictproxyobject.py --- a/pypy/module/cpyext/dictproxyobject.py +++ b/pypy/module/cpyext/dictproxyobject.py @@ -1,67 +1,7 @@ -# Read-only proxy for mappings. PyPy does not have a separate type for -# type.__dict__, so PyDictProxy_New has to use a custom read-only mapping. - -from pypy.interpreter.baseobjspace import W_Root -from pypy.interpreter.gateway import unwrap_spec, WrappedDefault -from pypy.interpreter.typedef import TypeDef, interp2app +from pypy.objspace.std.dictproxyobject import W_DictProxyObject from pypy.module.cpyext.api import cpython_api, build_type_checkers from pypy.module.cpyext.pyobject import PyObject -class W_DictProxyObject(W_Root): - "Read-only proxy for mappings." - - def __init__(self, w_mapping): - self.w_mapping = w_mapping - - def descr_len(self, space): - return space.len(self.w_mapping) - - def descr_getitem(self, space, w_key): - return space.getitem(self.w_mapping, w_key) - - def descr_contains(self, space, w_key): - return space.contains(self.w_mapping, w_key) - - def descr_iter(self, space): - return space.iter(self.w_mapping) - - def descr_str(self, space): - return space.str(self.w_mapping) - - def descr_repr(self, space): - return space.repr(self.w_mapping) - - @unwrap_spec(w_default=WrappedDefault(None)) - def get_w(self, space, w_key, w_default): - return space.call_method(self.w_mapping, "get", w_key, w_default) - - def keys_w(self, space): - return space.call_method(self.w_mapping, "keys") - - def values_w(self, space): - return space.call_method(self.w_mapping, "values") - - def items_w(self, space): - return space.call_method(self.w_mapping, "items") - - def copy_w(self, space): - return space.call_method(self.w_mapping, "copy") - -W_DictProxyObject.typedef = TypeDef( - 'mappingproxy', - __len__=interp2app(W_DictProxyObject.descr_len), - __getitem__=interp2app(W_DictProxyObject.descr_getitem), - __contains__=interp2app(W_DictProxyObject.descr_contains), - __iter__=interp2app(W_DictProxyObject.descr_iter), - __str__=interp2app(W_DictProxyObject.descr_str), - __repr__=interp2app(W_DictProxyObject.descr_repr), - get=interp2app(W_DictProxyObject.get_w), - keys=interp2app(W_DictProxyObject.keys_w), - values=interp2app(W_DictProxyObject.values_w), - items=interp2app(W_DictProxyObject.items_w), - copy=interp2app(W_DictProxyObject.copy_w) -) - PyDictProxy_Check, PyDictProxy_CheckExact = build_type_checkers( "DictProxy", W_DictProxyObject) 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 @@ -277,9 +277,41 @@ args->ob_type->tp_dict, "copy"); Py_INCREF(method); return method; - ''')]) + '''), + ("get_type_dict", "METH_O", + ''' + PyObject* value = args->ob_type->tp_dict; + if (value == NULL) value = Py_None; + Py_INCREF(value); + return value; + '''), + ]) obj = foo.new() assert module.read_tp_dict(obj) == foo.fooType.copy + d = module.get_type_dict(obj) + assert type(d) is dict + d["_some_attribute"] = 1 + assert type(obj)._some_attribute == 1 + del d["_some_attribute"] + + class A(object): + pass + obj = A() + d = module.get_type_dict(obj) + assert type(d) is dict + d["_some_attribute"] = 1 + assert type(obj)._some_attribute == 1 + del d["_some_attribute"] + + d = module.get_type_dict(1) + assert type(d) is dict + try: + d["_some_attribute"] = 1 + except TypeError: # on PyPy, int.__dict__ is really immutable + pass + else: + assert int._some_attribute == 1 + del d["_some_attribute"] def test_custom_allocation(self): foo = self.import_module("foo") @@ -348,6 +380,21 @@ api.Py_DecRef(ref) + def test_type_dict(self, space, api): + w_class = space.appexec([], """(): + class A(object): + pass + return A + """) + ref = make_ref(space, w_class) + + py_type = rffi.cast(PyTypeObjectPtr, ref) + w_dict = from_ref(space, py_type.c_tp_dict) + w_name = space.newunicode(u'a') + space.setitem(w_dict, w_name, space.wrap(1)) + assert space.int_w(space.getattr(w_class, w_name)) == 1 + space.delitem(w_dict, w_name) + def test_multiple_inheritance(self, space, api): w_class = space.appexec([], """(): class A(object): 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 @@ -192,7 +192,7 @@ py_methoddescr.c_d_method = w_obj.ml def classmethoddescr_realize(space, obj): - # XXX NOT TESTED When is this ever called? + # XXX NOT TESTED When is this ever called? method = rffi.cast(lltype.Ptr(PyMethodDef), obj) w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type)) w_obj = space.allocate_instance(W_PyCClassMethodObject, w_type) @@ -201,7 +201,7 @@ return w_obj def methoddescr_realize(space, obj): - # XXX NOT TESTED When is this ever called? + # XXX NOT TESTED When is this ever called? method = rffi.cast(lltype.Ptr(PyMethodDef), obj) w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type)) w_obj = space.allocate_instance(W_PyCMethodObject, w_type) @@ -272,12 +272,12 @@ if len(slot_names) == 1: if not getattr(pto, slot_names[0]): setattr(pto, slot_names[0], slot_func_helper) - elif (w_type.getname(space) in ('list', 'tuple') and + elif (w_type.getname(space) in ('list', 'tuple') and slot_names[0] == 'c_tp_as_number'): # XXX hack - hwo can we generalize this? The problem is method # names like __mul__ map to more than one slot, and we have no # convenient way to indicate which slots CPython have filled - # + # # We need at least this special case since Numpy checks that # (list, tuple) do __not__ fill tp_as_number pass @@ -767,8 +767,8 @@ if w_obj.is_cpytype(): Py_DecRef(space, pto.c_tp_dict) - w_dict = w_obj.getdict(space) - pto.c_tp_dict = make_ref(space, w_dict) + w_dict = w_obj.getdict(space) + pto.c_tp_dict = make_ref(space, w_dict) @cpython_api([PyTypeObjectPtr, PyTypeObjectPtr], rffi.INT_real, error=CANNOT_FAIL) def PyType_IsSubtype(space, a, b): diff --git a/pypy/objspace/std/classdict.py b/pypy/objspace/std/classdict.py new file mode 100644 --- /dev/null +++ b/pypy/objspace/std/classdict.py @@ -0,0 +1,119 @@ +from rpython.rlib import rerased +from rpython.rlib.objectmodel import iteritems_with_hash + +from pypy.interpreter.error import OperationError, oefmt +from pypy.objspace.std.dictmultiobject import ( + DictStrategy, create_iterator_classes) +from pypy.objspace.std.typeobject import unwrap_cell + + +class ClassDictStrategy(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 getitem(self, w_dict, w_key): + space = self.space + w_lookup_type = space.type(w_key) + if space.issubtype_w(w_lookup_type, space.w_unicode): + return self.getitem_str(w_dict, space.str_w(w_key)) + else: + return None + + def getitem_str(self, w_dict, key): + return self.unerase(w_dict.dstorage).getdictvalue(self.space, key) + + def setitem(self, w_dict, w_key, w_value): + space = self.space + if space.is_w(space.type(w_key), space.w_unicode): + self.setitem_str(w_dict, self.space.str_w(w_key), w_value) + else: + raise oefmt(space.w_TypeError, + "cannot add non-string keys to dict of a type") + + def setitem_str(self, w_dict, key, w_value): + w_type = self.unerase(w_dict.dstorage) + try: + w_type.setdictvalue(self.space, key, w_value) + except OperationError as e: + if not e.match(self.space, self.space.w_TypeError): + raise + if not w_type.is_cpytype(): + raise + # 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): + w_result = self.getitem(w_dict, w_key) + if w_result is not None: + return w_result + self.setitem(w_dict, w_key, w_default) + return w_default + + def delitem(self, w_dict, w_key): + space = self.space + w_key_type = space.type(w_key) + if space.is_w(w_key_type, space.w_unicode): + key = self.space.str_w(w_key) + if not self.unerase(w_dict.dstorage).deldictvalue(space, key): + raise KeyError + else: + raise KeyError + + def length(self, w_dict): + return len(self.unerase(w_dict.dstorage).dict_w) + + def w_keys(self, w_dict): + space = self.space + w_type = self.unerase(w_dict.dstorage) + return space.newlist([_wrapkey(space, key) + for key in w_type.dict_w.iterkeys()]) + + def values(self, w_dict): + return [unwrap_cell(self.space, w_value) for w_value in + self.unerase(w_dict.dstorage).dict_w.itervalues()] + + def items(self, w_dict): + space = self.space + w_type = self.unerase(w_dict.dstorage) + return [space.newtuple([_wrapkey(space, key), + unwrap_cell(space, w_value)]) + for (key, w_value) in w_type.dict_w.iteritems()] + + def clear(self, w_dict): + space = self.space + w_type = self.unerase(w_dict.dstorage) + if not w_type.is_heaptype(): + raise oefmt(space.w_TypeError, + "can't clear dictionary of type '%N'", w_type) + w_type.dict_w.clear() + w_type.mutated(None) + + def getiterkeys(self, w_dict): + return self.unerase(w_dict.dstorage).dict_w.iterkeys() + + def getitervalues(self, w_dict): + return self.unerase(w_dict.dstorage).dict_w.itervalues() + + def getiteritems_with_hash(self, w_dict): + return iteritems_with_hash(self.unerase(w_dict.dstorage).dict_w) + + def wrapkey(space, key): + return _wrapkey(space, key) + + def wrapvalue(space, value): + return unwrap_cell(space, value) + +def _wrapkey(space, key): + # keys are utf-8 encoded identifiers from type's dict_w + return space.wrap(key.decode('utf-8')) + +create_iterator_classes(ClassDictStrategy) 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,212 +1,95 @@ -from rpython.rlib import rerased -from rpython.rlib.objectmodel import iteritems_with_hash +""" +Read-only proxy for mappings. -from pypy.interpreter.error import OperationError, oefmt -from pypy.interpreter.gateway import interp2app -from pypy.interpreter.typedef import TypeDef -from pypy.objspace.std.dictmultiobject import ( - DictStrategy, W_DictObject, create_iterator_classes) -from pypy.objspace.std.typeobject import unwrap_cell +Its main use is as the return type of cls.__dict__. +""" +from pypy.interpreter.baseobjspace import W_Root +from pypy.interpreter.error import oefmt +from pypy.interpreter.gateway import unwrap_spec, WrappedDefault +from pypy.interpreter.typedef import TypeDef, interp2app -class W_DictProxyObject(W_DictObject): +class W_DictProxyObject(W_Root): + "Read-only proxy for mappings." + + def __init__(self, w_mapping): + self.w_mapping = w_mapping + @staticmethod def descr_new(space, w_type, w_mapping): if (not space.lookup(w_mapping, "__getitem__") or - space.isinstance_w(w_mapping, space.w_list) or - space.isinstance_w(w_mapping, space.w_tuple)): + space.isinstance_w(w_mapping, space.w_list) or + space.isinstance_w(w_mapping, space.w_tuple)): raise oefmt(space.w_TypeError, - "mappingproxy() argument must be a mapping, not %T", 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 + "mappingproxy() argument must be a mapping, not %T", + w_mapping) + return W_DictProxyObject(w_mapping) def descr_init(self, space, __args__): pass + def descr_len(self, space): + return space.len(self.w_mapping) + + def descr_getitem(self, space, w_key): + return space.getitem(self.w_mapping, w_key) + + def descr_contains(self, space, w_key): + return space.contains(self.w_mapping, w_key) + + def descr_iter(self, space): + return space.iter(self.w_mapping) + + def descr_str(self, space): + return space.str(self.w_mapping) + def descr_repr(self, space): - return space.wrap(u"mappingproxy(%s)" % ( - space.unicode_w(W_DictObject.descr_repr(self, space)))) + return space.newunicode(u"mappingproxy(%s)" % + (space.unicode_w(space.repr(self.w_mapping)),)) + + @unwrap_spec(w_default=WrappedDefault(None)) + def get_w(self, space, w_key, w_default): + return space.call_method(self.w_mapping, "get", w_key, w_default) + + def keys_w(self, space): + return space.call_method(self.w_mapping, "keys") + + def values_w(self, space): + return space.call_method(self.w_mapping, "values") + + def items_w(self, space): + return space.call_method(self.w_mapping, "items") + + def copy_w(self, space): + return space.call_method(self.w_mapping, "copy") + +cmp_methods = {} +def make_cmp_method(op): + def descr_op(self, space, w_other): + return getattr(space, op)(self.w_mapping, w_other) + descr_name = 'descr_' + op + descr_op.__name__ = descr_name + setattr(W_DictProxyObject, descr_name, descr_op) + cmp_methods['__%s__' % op] = interp2app(getattr(W_DictProxyObject, descr_name)) + +for op in ['eq', 'ne', 'gt', 'ge', 'lt', 'le']: + make_cmp_method(op) + W_DictProxyObject.typedef = TypeDef( - "mappingproxy", W_DictObject.typedef, - __new__ = interp2app(W_DictProxyObject.descr_new), - __init__ = interp2app(W_DictProxyObject.descr_init), - __repr__ = interp2app(W_DictProxyObject.descr_repr), + 'mappingproxy', + __new__=interp2app(W_DictProxyObject.descr_new), + __init__=interp2app(W_DictProxyObject.descr_init), + __len__=interp2app(W_DictProxyObject.descr_len), + __getitem__=interp2app(W_DictProxyObject.descr_getitem), + __contains__=interp2app(W_DictProxyObject.descr_contains), + __iter__=interp2app(W_DictProxyObject.descr_iter), + __str__=interp2app(W_DictProxyObject.descr_str), + __repr__=interp2app(W_DictProxyObject.descr_repr), + get=interp2app(W_DictProxyObject.get_w), + keys=interp2app(W_DictProxyObject.keys_w), + values=interp2app(W_DictProxyObject.values_w), + items=interp2app(W_DictProxyObject.items_w), + copy=interp2app(W_DictProxyObject.copy_w), + **cmp_methods ) - - -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 getitem(self, w_dict, w_key): - space = self.space - w_lookup_type = space.type(w_key) - if space.issubtype_w(w_lookup_type, space.w_unicode): - return self.getitem_str(w_dict, space.str_w(w_key)) - else: - return None - - def getitem_str(self, w_dict, key): - return self.unerase(w_dict.dstorage).getdictvalue(self.space, key) - - def setitem(self, w_dict, w_key, w_value): - space = self.space - if space.is_w(space.type(w_key), space.w_unicode): - self.setitem_str(w_dict, self.space.str_w(w_key), w_value) - else: - raise oefmt(space.w_TypeError, - "cannot add non-string keys to dict of a type") - - def setitem_str(self, w_dict, key, w_value): - w_type = self.unerase(w_dict.dstorage) - try: - w_type.setdictvalue(self.space, key, w_value) - except OperationError as e: - if not e.match(self.space, self.space.w_TypeError): - raise - if not w_type.is_cpytype(): - raise - # 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): - w_result = self.getitem(w_dict, w_key) - if w_result is not None: - return w_result - self.setitem(w_dict, w_key, w_default) - return w_default - - def delitem(self, w_dict, w_key): - space = self.space - w_key_type = space.type(w_key) - if space.is_w(w_key_type, space.w_unicode): - key = self.space.str_w(w_key) - if not self.unerase(w_dict.dstorage).deldictvalue(space, key): - raise KeyError - else: - raise KeyError - - def length(self, w_dict): - return len(self.unerase(w_dict.dstorage).dict_w) - - def w_keys(self, w_dict): - space = self.space - w_type = self.unerase(w_dict.dstorage) - return space.newlist([_wrapkey(space, key) - for key in w_type.dict_w.iterkeys()]) - - def values(self, w_dict): - return [unwrap_cell(self.space, w_value) for w_value in self.unerase(w_dict.dstorage).dict_w.itervalues()] - - def items(self, w_dict): - space = self.space - w_type = self.unerase(w_dict.dstorage) - return [space.newtuple([_wrapkey(space, key), - unwrap_cell(space, w_value)]) - for (key, w_value) in w_type.dict_w.iteritems()] - - def clear(self, w_dict): - space = self.space - w_type = self.unerase(w_dict.dstorage) - if not w_type.is_heaptype(): - raise oefmt(space.w_TypeError, - "can't clear dictionary of type '%N'", w_type) - w_type.dict_w.clear() - w_type.mutated(None) - - def getiterkeys(self, w_dict): - return self.unerase(w_dict.dstorage).dict_w.iterkeys() - def getitervalues(self, w_dict): - return self.unerase(w_dict.dstorage).dict_w.itervalues() - def getiteritems_with_hash(self, w_dict): - return iteritems_with_hash(self.unerase(w_dict.dstorage).dict_w) - def wrapkey(space, key): - return _wrapkey(space, key) - def wrapvalue(space, value): - return unwrap_cell(space, value) - -def _wrapkey(space, key): - # keys are utf-8 encoded identifiers from type's dict_w - 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): - try: - return self.space.getitem(self.unerase(w_dict.dstorage), w_key) - except OperationError as e: - if not e.match(self.space, self.space.w_KeyError): - raise - return None - - 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_with_hash(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") - - def copy(self, w_dict): - return self.space.call_method(self.unerase(w_dict.dstorage), "copy") - -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 @@ -9,42 +9,20 @@ assert 'a' in NotEmpty.__dict__ assert 'a' in NotEmpty.__dict__.keys() assert 'b' not in NotEmpty.__dict__ - NotEmpty.__dict__['b'] = 4 - assert NotEmpty.b == 4 - del NotEmpty.__dict__['b'] assert NotEmpty.__dict__.get("b") is None + raises(TypeError, "NotEmpty.__dict__['b'] = 4") raises(TypeError, 'NotEmpty.__dict__[15] = "y"') - raises(KeyError, 'del NotEmpty.__dict__[15]') + raises(TypeError, 'del NotEmpty.__dict__[15]') - assert NotEmpty.__dict__.setdefault("string", 1) == 1 - assert NotEmpty.__dict__.setdefault("string", 2) == 1 - assert NotEmpty.string == 1 - raises(TypeError, 'NotEmpty.__dict__.setdefault(15, 1)') - - def test_dictproxy_popitem(self): - class A(object): - a = 42 - seen = 0 - try: - while True: - key, value = A.__dict__.popitem() - if key == 'a': - assert value == 42 - seen += 1 - except KeyError: - pass - assert seen == 1 + raises(AttributeError, 'NotEmpty.__dict__.setdefault') def test_dictproxy_getitem(self): class NotEmpty(object): a = 1 assert 'a' in NotEmpty.__dict__ - class substr(str): pass + class substr(str): + pass assert substr('a') in NotEmpty.__dict__ - # the following are only for py2 - ## assert u'a' in NotEmpty.__dict__ - ## assert NotEmpty.__dict__[u'a'] == 1 - ## assert u'\xe9' not in NotEmpty.__dict__ def test_dictproxyeq(self): class a(object): @@ -63,9 +41,9 @@ class a(object): pass s1 = repr(a.__dict__) + assert s1.startswith('mappingproxy({') and s1.endswith('})') s2 = str(a.__dict__) - assert s1 == s2 - assert s1.startswith('mappingproxy({') and s1.endswith('})') + assert s1 == 'mappingproxy(%s)' % s2 def test_immutable_dict_on_builtin_type(self): raises(TypeError, "int.__dict__['a'] = 1") @@ -100,4 +78,3 @@ class AppTestUserObjectMethodCache(AppTestUserObject): spaceconfig = {"objspace.std.withmethodcachecounter": True} - diff --git a/pypy/objspace/std/test/test_typeobject.py b/pypy/objspace/std/test/test_typeobject.py --- a/pypy/objspace/std/test/test_typeobject.py +++ b/pypy/objspace/std/test/test_typeobject.py @@ -970,7 +970,6 @@ raises(TypeError, setattr, list, 'foobar', 42) raises(TypeError, delattr, dict, 'keys') raises(TypeError, 'int.__dict__["a"] = 1') - raises(TypeError, 'int.__dict__.clear()') def test_nontype_in_mro(self): class OldStyle: @@ -1028,10 +1027,9 @@ pass a = A() + d = A.__dict__ A.x = 1 - assert A.__dict__["x"] == 1 - A.__dict__['x'] = 5 - assert A.x == 5 + assert d["x"] == 1 def test_we_already_got_one_1(self): # Issue #2079: highly obscure: CPython complains if we say 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 @@ -4,8 +4,8 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.function import ( Function, StaticMethod, ClassMethod, FunctionWithFixedCode) -from pypy.interpreter.typedef import weakref_descr, GetSetProperty,\ - descr_get_dict, dict_descr, Member, TypeDef +from pypy.interpreter.typedef import ( + weakref_descr, GetSetProperty, dict_descr, Member, TypeDef) from pypy.interpreter.astcompiler.misc import mangle from pypy.module.__builtin__ import abstractinst @@ -344,7 +344,7 @@ def deldictvalue(self, space, key): if self.lazyloaders: self._cleanup_() # force un-lazification - if not self.is_heaptype(): + if not (self.is_heaptype() or self.is_cpytype()): raise oefmt(space.w_TypeError, "can't delete attributes on type object '%N'", self) try: @@ -483,14 +483,14 @@ self.getdictvalue(self.space, attr) del self.lazyloaders - def getdict(self, space): # returning a dict-proxy! - from pypy.objspace.std.dictproxyobject import DictProxyStrategy - from pypy.objspace.std.dictproxyobject import W_DictProxyObject + def getdict(self, space): + from pypy.objspace.std.classdict import ClassDictStrategy + from pypy.objspace.std.dictmultiobject import W_DictObject if self.lazyloaders: self._cleanup_() # force un-lazification - strategy = space.fromcache(DictProxyStrategy) + strategy = space.fromcache(ClassDictStrategy) storage = strategy.erase(self) - return W_DictProxyObject(space, strategy, storage) + return W_DictObject(space, strategy, storage) def is_heaptype(self): return self.flag_heaptype @@ -929,6 +929,13 @@ return space.newbool( abstractinst.p_recursive_isinstance_type_w(space, w_inst, w_obj)) +def type_get_dict(space, w_cls): + from pypy.objspace.std.dictproxyobject import W_DictProxyObject + w_dict = w_cls.getdict(space) + if w_dict is None: + return space.w_None + return W_DictProxyObject(w_dict) + W_TypeObject.typedef = TypeDef("type", __new__ = gateway.interp2app(descr__new__), __name__ = GetSetProperty(descr_get__name__, descr_set__name__), @@ -936,7 +943,7 @@ __bases__ = GetSetProperty(descr_get__bases__, descr_set__bases__), __base__ = GetSetProperty(descr__base), __mro__ = GetSetProperty(descr_get__mro__), - __dict__ = GetSetProperty(descr_get_dict), + __dict__=GetSetProperty(type_get_dict), __doc__ = GetSetProperty(descr__doc, descr_set__doc), __dir__ = gateway.interp2app(descr__dir), mro = gateway.interp2app(descr_mro), _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit