[pypy-svn] r34897 - in pypy/dist/pypy/objspace/std: . test
mwh at codespeak.net
mwh at codespeak.net
Thu Nov 23 15:22:39 CET 2006
Author: mwh
Date: Thu Nov 23 15:22:36 2006
New Revision: 34897
Modified:
pypy/dist/pypy/objspace/std/test/test_typeobject.py
pypy/dist/pypy/objspace/std/typeobject.py
pypy/dist/pypy/objspace/std/typetype.py
Log:
issue204 testing
implement assignment to __bases__
introduces some amount of code duplication which could or should be reduced.
Modified: pypy/dist/pypy/objspace/std/test/test_typeobject.py
==============================================================================
--- pypy/dist/pypy/objspace/std/test/test_typeobject.py (original)
+++ pypy/dist/pypy/objspace/std/test/test_typeobject.py Thu Nov 23 15:22:36 2006
@@ -88,7 +88,205 @@
assert Y.__bases__ == (X,)
class Z(Y,X): pass
assert Z.__bases__ == (Y, X)
-
+
+ Z.__bases__ = (X,)
+ #print Z.__bases__
+ assert Z.__bases__ == (X,)
+
+ def test_mutable_bases(self):
+ # from CPython's test_descr
+ class C(object):
+ pass
+ class C2(object):
+ def __getattribute__(self, attr):
+ if attr == 'a':
+ return 2
+ else:
+ return super(C2, self).__getattribute__(attr)
+ def meth(self):
+ return 1
+ class D(C):
+ pass
+ class E(D):
+ pass
+ d = D()
+ e = E()
+ D.__bases__ = (C,)
+ D.__bases__ = (C2,)
+ #import pdb; pdb.set_trace()
+ assert d.meth() == 1
+ assert e.meth() == 1
+ assert d.a == 2
+ assert e.a == 2
+ assert C2.__subclasses__() == [D]
+
+ # stuff that shouldn't:
+ class L(list):
+ pass
+
+ try:
+ L.__bases__ = (dict,)
+ except TypeError:
+ pass
+ else:
+ assert 0, "shouldn't turn list subclass into dict subclass"
+
+ try:
+ list.__bases__ = (dict,)
+ except TypeError:
+ pass
+ else:
+ assert 0, "shouldn't be able to assign to list.__bases__"
+
+ try:
+ D.__bases__ = (C2, list)
+ except TypeError:
+ pass
+ else:
+ assert 0, "best_base calculation found wanting"
+
+ try:
+ del D.__bases__
+ except (TypeError, AttributeError):
+ pass
+ else:
+ assert 0, "shouldn't be able to delete .__bases__"
+
+ try:
+ D.__bases__ = ()
+ except TypeError, msg:
+ if str(msg) == "a new-style class can't have only classic bases":
+ assert 0, "wrong error message for .__bases__ = ()"
+ else:
+ assert 0, "shouldn't be able to set .__bases__ to ()"
+
+ try:
+ D.__bases__ = (D,)
+ except TypeError:
+ pass
+ else:
+ # actually, we'll have crashed by here...
+ assert 0, "shouldn't be able to create inheritance cycles"
+
+ try:
+ D.__bases__ = (C, C)
+ except TypeError:
+ pass
+ else:
+ assert 0, "didn't detect repeated base classes"
+
+ try:
+ D.__bases__ = (E,)
+ except TypeError:
+ pass
+ else:
+ assert 0, "shouldn't be able to create inheritance cycles"
+
+ # let's throw a classic class into the mix:
+ try:
+ class Classic:
+ __metaclass__ = _classobj
+ def meth2(self):
+ return 3
+ except NameError:
+ class Classic:
+ def meth2(self):
+ return 3
+
+ D.__bases__ = (C, Classic)
+
+ assert d.meth2() == 3
+ assert e.meth2() == 3
+ try:
+ d.a
+ except AttributeError:
+ pass
+ else:
+ assert 0, "attribute should have vanished"
+
+ try:
+ D.__bases__ = (Classic,)
+ except TypeError:
+ pass
+ else:
+ assert 0, "new-style class must have a new-style base"
+
+ def test_mutable_bases_with_failing_mro(self):
+ class WorkOnce(type):
+ def __new__(self, name, bases, ns):
+ self.flag = 0
+ return super(WorkOnce, self).__new__(WorkOnce, name, bases, ns)
+ def mro(instance):
+ if instance.flag > 0:
+ raise RuntimeError, "bozo"
+ else:
+ instance.flag += 1
+ return type.mro(instance)
+
+ class WorkAlways(type):
+ def mro(self):
+ # this is here to make sure that .mro()s aren't called
+ # with an exception set (which was possible at one point).
+ # An error message will be printed in a debug build.
+ # What's a good way to test for this?
+ return type.mro(self)
+
+ class C(object):
+ pass
+
+ class C2(object):
+ pass
+
+ class D(C):
+ pass
+
+ class E(D):
+ pass
+
+ class F(D):
+ __metaclass__ = WorkOnce
+
+ class G(D):
+ __metaclass__ = WorkAlways
+
+ # Immediate subclasses have their mro's adjusted in alphabetical
+ # order, so E's will get adjusted before adjusting F's fails. We
+ # check here that E's gets restored.
+
+ E_mro_before = E.__mro__
+ D_mro_before = D.__mro__
+
+ try:
+ D.__bases__ = (C2,)
+ except RuntimeError:
+ assert D.__mro__ == D_mro_before
+ assert E.__mro__ == E_mro_before
+ else:
+ assert 0, "exception not propagated"
+
+ def test_mutable_bases_catch_mro_conflict(self):
+ class A(object):
+ pass
+
+ class B(object):
+ pass
+
+ class C(A, B):
+ pass
+
+ class D(A, B):
+ pass
+
+ class E(C, D):
+ pass
+
+ try:
+ C.__bases__ = (B, A)
+ except TypeError:
+ pass
+ else:
+ raise TestFailed, "didn't catch MRO conflict"
+
def test_builtin_add(self):
x = 5
assert x.__add__(6) == 11
Modified: pypy/dist/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/dist/pypy/objspace/std/typeobject.py (original)
+++ pypy/dist/pypy/objspace/std/typeobject.py Thu Nov 23 15:22:36 2006
@@ -342,6 +342,16 @@
else:
w_self.weak_subclasses_w.append(w_newref)
+ def remove_subclass(w_self, w_subclass):
+ space = w_self.space
+
+ for i in range(len(w_self.weak_subclasses_w)):
+ w_ref = w_self.weak_subclasses_w[i]
+ ob = space.call_function(w_ref)
+ if space.is_w(ob, w_subclass):
+ del w_self.weak_subclasses_w[i]
+ return
+
# for now, weakref support for W_TypeObject is hard to get automatically
_lifeline_ = None
def getweakref(self):
Modified: pypy/dist/pypy/objspace/std/typetype.py
==============================================================================
--- pypy/dist/pypy/objspace/std/typetype.py (original)
+++ pypy/dist/pypy/objspace/std/typetype.py Thu Nov 23 15:22:36 2006
@@ -1,5 +1,6 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter import gateway
+from pypy.interpreter.argument import Arguments
from pypy.interpreter.typedef import weakref_descr
from pypy.objspace.std.stdtypedef import *
@@ -84,14 +85,118 @@
w_type = _check(space, w_type,"expected type")
return space.newlist(w_type.compute_mro())
-def descr__bases(space, w_type):
+def descr_get__bases__(space, w_type):
w_type = _check(space, w_type)
- from pypy.objspace.std.typeobject import W_TypeObject
- if not isinstance(w_type, W_TypeObject):
- raise OperationError(space.w_TypeError,
- space.wrap("descriptor is for 'type'"))
return space.newtuple(w_type.bases_w)
+def mro_subclasses(space, w_type, temp):
+ from pypy.objspace.std.typeobject import W_TypeObject
+ if not w_type.weak_subclasses_w:
+ return
+ for w_ref in w_type.weak_subclasses_w:
+ w_sc = space.call_function(w_ref)
+ if not space.is_w(w_sc, space.w_None):
+ assert isinstance(w_sc, W_TypeObject)
+ temp.append((w_sc, w_sc.mro_w))
+ mro_internal(space, w_sc)
+ mro_subclasses(space, w_sc, temp)
+
+# should be a W_TypeObject method i guess
+def mro_internal(space, w_type):
+ if not space.is_w(space.type(w_type), space.w_type):
+ #w_type.mro_w = []
+ mro_func = space.type(w_type).lookup('mro')
+ mro_func_args = Arguments(space, [w_type])
+ w_mro = space.call_args(mro_func, mro_func_args)
+ w_type.mro_w = space.unpackiterable(w_mro)
+ # do some checking here
+ else:
+ w_type.mro_w = w_type.compute_mro()
+
+def best_base(space, newstyle_bases_w):
+ if not newstyle_bases_w:
+ raise OperationError(space.w_TypeError,
+ space.wrap("a new-style class can't have only classic bases"))
+ w_bestbase = None
+ w_winner = None
+ for w_base in newstyle_bases_w:
+ w_candidate = w_base.get_layout()
+ if w_winner is None:
+ w_winner = w_candidate
+ w_bestbase = w_base
+ elif space.is_true(space.issubtype(w_winner, w_candidate)):
+ pass
+ elif space.is_true(space.issubtype(w_candidate, w_winner)):
+ w_winner = w_candidate
+ w_bestbase = w_base
+ else:
+ raise OperationError(space.w_TypeError,
+ space.wrap("multiple bases have instance lay-out conflict"))
+ return w_bestbase
+
+def descr_set__bases__(space, w_type, w_value):
+ from pypy.objspace.std.typeobject import W_TypeObject
+ # this assumes all app-level type objects are W_TypeObject
+ w_type = _check(space, w_type)
+ if not w_type.is_heaptype():
+ raise OperationError(space.w_TypeError,
+ space.wrap("can't set %s.__bases__" %
+ w_type.name))
+ if not space.is_true(space.isinstance(w_value, space.w_tuple)):
+ raise OperationError(space.w_TypeError,
+ space.wrap("can only assign tuple to %s.__bases__, not %s"%
+ (w_type.name, space.type(w_value).name)))
+ if space.int_w(space.len(w_value)) == 0:
+ raise OperationError(space.w_TypeError,
+ space.wrap("can only assign non-empty tuple to %s.__bases__, not ()"%
+ w_type.name))
+ new_newstyle_bases = []
+ for w_base in space.unpackiterable(w_value):
+ if not isinstance(w_base, W_TypeObject):
+ w_typ = space.type(w_base)
+ if not space.is_w(w_typ, space.w_classobj):
+ raise OperationError(space.w_TypeError,
+ space.wrap("%s.__bases__ must be tuple of old- or new-style classes, not '%s'"%
+ (w_type.name, w_typ.name)))
+ else:
+ new_newstyle_bases.append(w_base)
+ if space.is_true(space.issubtype(w_base, w_type)):
+ raise OperationError(space.w_TypeError,
+ space.wrap("a __bases__ item causes an inheritance cycle"))
+
+ new_base = best_base(space, new_newstyle_bases)
+
+ if w_type.w_bestbase.get_full_instance_layout() != new_base.get_full_instance_layout():
+ raise OperationError(space.w_TypeError,
+ space.wrap("__bases__ assignment: '%s' object layout differs from '%s'" %
+ (w_type.getname(space, '?'), new_base.getname(space, '?'))))
+
+ saved_bases = w_type.bases_w
+ saved_base = w_type.w_bestbase
+ saved_mro = w_type.mro_w
+
+ w_type.bases_w = space.unpackiterable(w_value)
+ w_type.w_bestbase = new_base
+
+ temp = []
+ try:
+ mro_internal(space, w_type)
+
+ mro_subclasses(space, w_type, temp)
+
+ for old_base in saved_bases:
+ if isinstance(old_base, W_TypeObject):
+ old_base.remove_subclass(w_type)
+ for new_base in new_newstyle_bases:
+ new_base.add_subclass(w_type)
+ except:
+ for cls, old_mro in temp:
+ cls.mro_w = old_mro
+ w_type.bases_w = saved_bases
+ w_type.w_bestbase = saved_base
+ w_type.mro_w = saved_mro
+ raise
+
def descr__base(space, w_type):
w_type = _check(space, w_type)
if w_type.w_bestbase is not None:
@@ -155,7 +260,7 @@
type_typedef = StdTypeDef("type",
__new__ = newmethod(descr__new__),
__name__ = GetSetProperty(descr_get__name__, descr_set__name__),
- __bases__ = GetSetProperty(descr__bases),
+ __bases__ = GetSetProperty(descr_get__bases__, descr_set__bases__),
__base__ = GetSetProperty(descr__base),
__mro__ = GetSetProperty(descr_get__mro__),
__dict__ = GetSetProperty(descr_get_dict),
More information about the Pypy-commit
mailing list