Author: Yannick Jadoul <[email protected]>
Branch: py3.7
Changeset: r97729:02a99370f6b3
Date: 2019-09-24 17:27 +0200
http://bitbucket.org/pypy/pypy/changeset/02a99370f6b3/

Log:    Implemented __mro_entries__ from PEP 560

diff --git a/pypy/module/__builtin__/compiling.py 
b/pypy/module/__builtin__/compiling.py
--- a/pypy/module/__builtin__/compiling.py
+++ b/pypy/module/__builtin__/compiling.py
@@ -96,13 +96,33 @@
     frame = space.getexecutioncontext().gettopframe()
     frame.exec_(w_prog, w_globals, w_locals)
 
+def _update_bases(space, w_bases):
+    bases_w = space.listview(w_bases)
+    new_bases_w = []
+    changed = False
+    for w_base in bases_w:
+        w_meth = space.lookup(w_base, '__mro_entries__')
+        if w_meth is not None:
+            new_base_w = space.get_and_call_function(w_meth, w_base, w_bases)
+            if not space.isinstance_w(new_base_w, space.w_tuple):
+                raise oefmt(space.w_TypeError, "__mro_entries__ must return a 
tuple")
+            new_bases_w.extend(space.fixedview(new_base_w))
+            changed = True
+        else:
+            new_bases_w.append(w_base)
+    if not changed:
+        return bases_w
+    return new_bases_w
+
 def build_class(space, w_func, w_name, __args__):
     from pypy.objspace.std.typeobject import _calculate_metaclass, W_TypeObject
     from pypy.interpreter.nestedscope import Cell
     if not isinstance(w_func, Function):
         raise oefmt(space.w_TypeError, "__build_class__: func must be a 
function")
-    bases_w, kwds_w = __args__.unpack()
-    w_bases = space.newtuple(bases_w)
+    orig_bases_w, kwds_w = __args__.unpack()
+    w_orig_bases = space.newtuple(orig_bases_w)
+    bases_w = _update_bases(space, w_orig_bases)
+    w_bases = space.newtuple(bases_w[:])
     w_meta = kwds_w.pop('metaclass', None)
     if w_meta is not None:
         isclass = space.isinstance_w(w_meta, space.w_type)
@@ -144,6 +164,8 @@
     frame = space.createframe(code, w_func.w_func_globals, w_func)
     frame.setdictscope(w_namespace)
     w_cell = frame.run()
+    if bases_w is not orig_bases_w:
+        space.setitem(w_namespace, space.newtext("__orig_bases__"), 
w_orig_bases)
     keywords = kwds_w.keys()
     args = Arguments(space,
                      args_w=[w_name, w_bases, w_namespace],
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
@@ -1433,6 +1433,58 @@
         assert WithMetaclass[int] == "Metaclass[int]"
         """
 
+    def test_mro_entries(self):
+        """
+        class BaseA: pass
+        class BaseB: pass
+        class BaseC: pass
+        class BaseD: pass
+
+        class ProxyA:
+            def __mro_entries__(self, orig_bases):
+                return (BaseA,)
+        class ProxyAB:
+            def __mro_entries__(self, orig_bases):
+                return (BaseA, BaseB)
+        class ProxyNone:
+            def __mro_entries__(self, orig_bases):
+                return ()
+
+        class TestA(ProxyA()): pass
+        assert TestA.__bases__ == (BaseA,)
+        assert len(TestA.__orig_bases__) == 1
+        assert isinstance(TestA.__orig_bases__[0], ProxyA)
+
+        class TestAB(ProxyAB()): pass
+        assert TestAB.__bases__ == (BaseA, BaseB)
+        assert len(TestAB.__orig_bases__) == 1
+        assert isinstance(TestAB.__orig_bases__[0], ProxyAB)
+
+        class TestNone(ProxyNone()): pass
+        assert TestNone.__bases__ == (object,)
+        assert len(TestNone.__orig_bases__) == 1
+        assert isinstance(TestNone.__orig_bases__[0], ProxyNone)
+
+        class TestMixed(BaseC, ProxyAB(), BaseD, ProxyNone()): pass
+        assert TestMixed.__bases__ == (BaseC, BaseA, BaseB, BaseD)
+        assert len(TestMixed.__orig_bases__) == 4
+        assert isinstance(TestMixed.__orig_bases__[1], ProxyAB) and 
isinstance(TestMixed.__orig_bases__[3], ProxyNone)
+
+        with raises(TypeError) as excinfo:
+            class TestDuplicate(BaseB, ProxyAB()): pass
+        assert str(excinfo.value) == "duplicate base class 'BaseB'"
+
+        with raises(TypeError) as excinfo:
+            type('TestType', (BaseC, ProxyAB(), BaseD, ProxyNone()), {})
+        assert str(excinfo.value) == "type() doesn't support MRO entry 
resolution; use types.new_class()"
+
+        import types
+        TestTypesNewClass = types.new_class('TestTypesNewClass', (BaseC, 
ProxyAB(), BaseD, ProxyNone()), {})
+        assert TestMixed.__bases__ == (BaseC, BaseA, BaseB, BaseD)
+        assert len(TestMixed.__orig_bases__) == 4
+        assert isinstance(TestMixed.__orig_bases__[1], ProxyAB) and 
isinstance(TestMixed.__orig_bases__[3], ProxyNone)
+        """
+
 
 class AppTestWithMethodCacheCounter:
     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
@@ -808,6 +808,11 @@
     # above to be seen by the jit.
     _check_new_args(space, w_name, w_bases, w_dict)
     bases_w = space.fixedview(w_bases)
+    for w_base in bases_w:
+        if space.lookup(w_base, '__mro_entries__') is not None:
+            raise oefmt(space.w_TypeError,
+                        "type() doesn't support MRO entry resolution; "
+                        "use types.new_class()")
 
     w_winner = _calculate_metaclass(space, w_typetype, bases_w)
     if not space.is_w(w_winner, w_typetype):
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to