[pypy-svn] r4978 - pypy/branch/src-newobjectmodel/pypy/objspace/std

arigo at codespeak.net arigo at codespeak.net
Sat Jun 5 16:41:33 CEST 2004


Author: arigo
Date: Sat Jun  5 16:41:32 2004
New Revision: 4978

Modified:
   pypy/branch/src-newobjectmodel/pypy/objspace/std/mro.py
   pypy/branch/src-newobjectmodel/pypy/objspace/std/typeobject.py
   pypy/branch/src-newobjectmodel/pypy/objspace/std/typetype.py
Log:
The C3 algorithm to compute the Method Resolution Order now integrated. 


Modified: pypy/branch/src-newobjectmodel/pypy/objspace/std/mro.py
==============================================================================
--- pypy/branch/src-newobjectmodel/pypy/objspace/std/mro.py	(original)
+++ pypy/branch/src-newobjectmodel/pypy/objspace/std/mro.py	Sat Jun  5 16:41:32 2004
@@ -3,68 +3,55 @@
 """
 
 
-def MRO(cls):
-
-    def register(cls):
-        try:
-            blocklist = blocking[cls]
-        except KeyError:
-            blocklist = blocking[cls] = [0]
-            prevlist = blocklist
-            for base in cls.__bases__:
-                prevlist.append(base)
-                prevlist = register(base)
-        blocklist[0] += 1
-        return blocklist
 
+def mro(cls):
     order = []
-    blocking = {}
-    register(cls)
-    
-    unblock = [cls]
-    while unblock:
-        cls = unblock.pop()
-        blocklist = blocking[cls]
-        assert blocklist[0] > 0
-        blocklist[0] -= 1
-        if blocklist[0] == 0:
-            order.append(cls)
-            unblock += blocklist[:0:-1]
-
-    if len(order) < len(blocking):
-        mro_error(blocking)
+    orderlists = [mro(base) for base in cls.__bases__]
+    orderlists.append([cls] + list(cls.__bases__))
+    while orderlists:
+        for candidatelist in orderlists:
+            candidate = candidatelist[0]
+            if blockinglist(candidate, orderlists) is None:
+                break    # good candidate
+        else:
+            mro_error(orderlists)  # no candidate found
+        assert candidate not in order
+        order.append(candidate)
+        for i in range(len(orderlists)-1, -1, -1):
+            if orderlists[i][0] == candidate:
+                del orderlists[i][0]
+                if len(orderlists[i]) == 0:
+                    del orderlists[i]
     return order
 
-
-def mro_error(blocking):
-    # look for a cycle
-
-    def find_cycle(cls):
-        path.append(cls)
-        blocklist = blocking[cls]   # raise KeyError when we complete the path
-        if blocklist[0] > 0:
-            del blocking[cls]
-            for cls2 in blocklist[1:]:
-                find_cycle(cls2)
-            blocking[cls] = blocklist
-        del path[-1]
-
-    #import pprint; pprint.pprint(blocking)
-    path = []
-    try:
-        for cls in blocking.keys():
-            find_cycle(cls)
-    except KeyError:
-        i = path.index(path[-1])
-        names = [cls.__name__ for cls in path[i:]]
-        raise TypeError, "Cycle among base classes: " + ' < '.join(names)
-    else:
-        # should not occur
-        raise TypeError, "Cannot create a consistent method resolution order (MRO)"
+def blockinglist(candidate, orderlists):
+    for lst in orderlists:
+        if candidate in lst[1:]:
+            return lst
+    return None  # good candidate
+
+def mro_error(orderlists):
+    cycle = []
+    candidate = orderlists[0][0]
+    while candidate not in cycle:
+        cycle.append(candidate)
+        nextblockinglist = blockinglist(candidate, orderlists)
+        candidate = nextblockinglist[0]
+    del cycle[:cycle.index(candidate)]
+    cycle.append(candidate)
+    cycle.reverse()
+    names = [cls.__name__ for cls in cycle]
+    raise TypeError, "Cycle among base classes: " + ' < '.join(names)
+
+
+def mronames(cls):
+    names = [cls.__name__ for cls in mro(cls)]
+    return names
 
 
 if __name__ == '__main__':
     class ex_9:
+        #O = object
         class O: pass
         class A(O): pass
         class B(O): pass
@@ -76,5 +63,11 @@
         class K2(D,F,B,E): pass
         class K3(D,A): pass
         class Z(K1,K2,F,K3): pass
+        class ZM(K1,K2,K3): pass
+        #print ZM.__mro__
 
-    print MRO(ex_9.Z)
+    print 'K1:', mronames(ex_9.K1)
+    print 'K2:', mronames(ex_9.K2)
+    print 'K3:', mronames(ex_9.K3)
+    print mronames(ex_9.ZM)
+    print mronames(ex_9.Z)

Modified: pypy/branch/src-newobjectmodel/pypy/objspace/std/typeobject.py
==============================================================================
--- pypy/branch/src-newobjectmodel/pypy/objspace/std/typeobject.py	(original)
+++ pypy/branch/src-newobjectmodel/pypy/objspace/std/typeobject.py	Sat Jun  5 16:41:32 2004
@@ -10,6 +10,7 @@
         w_self.bases_w = bases_w
         w_self.dict_w = dict_w
         w_self.needs_new_dict = False
+        w_self.mro_w = compute_C3_mro(w_self)   # XXX call type(w_self).mro()
         if overridetypedef is not None:
             w_self.instancetypedef = overridetypedef
         else:
@@ -38,23 +39,13 @@
                 w_self.dict_w['__dict__'] = space.wrap(attrproperty_w('w__dict__'))
             elif nd:
                 w_self.needs_new_dict = True
-                
-            
-
-    def getmro(w_self):
-        # XXX this is something that works not too bad right now
-        # XXX do the complete mro thing later (see mro.py)
-        mro = [w_self]
-        for w_parent in w_self.bases_w:
-            mro += w_parent.getmro()
-        return mro
 
     def lookup(w_self, key):
         # note that this doesn't call __get__ on the result at all
         # XXX this should probably also return the (parent) class in which
         # the attribute was found
         space = w_self.space
-        for w_class in w_self.getmro():
+        for w_class in w_self.mro_w:
             try:
                 return w_class.dict_w[key]
             except KeyError:
@@ -103,7 +94,7 @@
     return w_newobject
 
 def issubtype__Type_Type(space, w_type1, w_type2):
-    return space.newbool(w_type2 in w_type1.getmro())
+    return space.newbool(w_type2 in w_type1.mro_w)
 
 def repr__Type(space, w_obj):
     return space.wrap("<pypy type '%s'>" % w_obj.name)  # XXX remove 'pypy'
@@ -133,5 +124,53 @@
 # XXX __delattr__
 # XXX __hash__ ??
 
+# ____________________________________________________________
+
+def compute_C3_mro(cls):
+    order = []
+    orderlists = [list(base.mro_w) for base in cls.bases_w]
+    orderlists.append([cls] + cls.bases_w)
+    while orderlists:
+        for candidatelist in orderlists:
+            candidate = candidatelist[0]
+            if mro_blockinglist(candidate, orderlists) is None:
+                break    # good candidate
+        else:
+            mro_error(orderlists)  # no candidate found
+        assert candidate not in order
+        order.append(candidate)
+        for i in range(len(orderlists)-1, -1, -1):
+            if orderlists[i][0] == candidate:
+                del orderlists[i][0]
+                if len(orderlists[i]) == 0:
+                    del orderlists[i]
+    return order
+
+def mro_blockinglist(candidate, orderlists):
+    for lst in orderlists:
+        if candidate in lst[1:]:
+            return lst
+    return None  # good candidate
+
+def mro_error(orderlists):
+    cycle = []
+    candidate = orderlists[-1][0]
+    space = candidate.space
+    if candidate in orderlists[-1][1:]:
+        # explicit error message for this specific case
+        raise OperationError(space.w_TypeError,
+            space.wrap("duplicate base class " + candidate.name))
+    while candidate not in cycle:
+        cycle.append(candidate)
+        nextblockinglist = mro_blockinglist(candidate, orderlists)
+        candidate = nextblockinglist[0]
+    del cycle[:cycle.index(candidate)]
+    cycle.append(candidate)
+    cycle.reverse()
+    names = [cls.name for cls in cycle]
+    raise OperationError(space.w_TypeError,
+        space.wrap("cycle among base classes: " + ' < '.join(names)))
+
+# ____________________________________________________________
 
 register_all(vars())

Modified: pypy/branch/src-newobjectmodel/pypy/objspace/std/typetype.py
==============================================================================
--- pypy/branch/src-newobjectmodel/pypy/objspace/std/typetype.py	(original)
+++ pypy/branch/src-newobjectmodel/pypy/objspace/std/typetype.py	Sat Jun  5 16:41:32 2004
@@ -21,7 +21,7 @@
 
 def descr_get__mro__(space, w_type):
     # XXX this should be inside typeobject.py
-    return space.newtuple(w_type.getmro())
+    return space.newtuple(w_type.mro_w)
 
 def descr__dict__(space, w_type):
     # XXX should return a <dictproxy object>



More information about the Pypy-commit mailing list