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