[pypy-commit] pypy default: A first version of import_from_mixin(), which really copies the

arigo noreply at buildbot.pypy.org
Tue Aug 20 13:49:21 CEST 2013


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r66252:51c5cb0ddc92
Date: 2013-08-20 13:48 +0200
http://bitbucket.org/pypy/pypy/changeset/51c5cb0ddc92/

Log:	A first version of import_from_mixin(), which really copies the
	content of the source class into the target, completely before
	translation.

diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -2539,6 +2539,27 @@
         s = a.build_types(f, [])
         assert s.const == 2
 
+    def test_import_from_mixin(self):
+        class M(object):
+            def f(self):
+                return self.a
+        class I(object):
+            objectmodel.import_from_mixin(M)
+            def __init__(self, i):
+                self.a = i
+        class S(object):
+            objectmodel.import_from_mixin(M)
+            def __init__(self, s):
+                self.a = s
+        def f(n):
+            return (I(n).f(), S("a" * n).f())
+
+        assert f(3) == (3, "aaa")
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [int])
+        assert isinstance(s.items[0], annmodel.SomeInteger)
+        assert isinstance(s.items[1], annmodel.SomeString)
+
     def test___class___attribute(self):
         class Base(object): pass
         class A(Base): pass
diff --git a/rpython/rlib/objectmodel.py b/rpython/rlib/objectmodel.py
--- a/rpython/rlib/objectmodel.py
+++ b/rpython/rlib/objectmodel.py
@@ -9,7 +9,7 @@
 import types
 import math
 import inspect
-from rpython.tool.sourcetools import rpython_wrapper
+from rpython.tool.sourcetools import rpython_wrapper, func_with_new_name
 
 # specialize is a decorator factory for attaching _annspecialcase_
 # attributes to functions: for example
@@ -720,3 +720,32 @@
         self.dic = dic
         self.key = key
         self.hash = hash
+
+# ____________________________________________________________
+
+def import_from_mixin(M):
+    flatten = {}
+    for base in inspect.getmro(M):
+        for key, value in base.__dict__.items():
+            if key in ('__module__', '__name__', '__dict__',
+                       '__doc__', '__weakref__'):
+                continue
+            if key in flatten:
+                continue
+            if isinstance(value, types.FunctionType):
+                value = func_with_new_name(value, value.__name__)
+            elif isinstance(value, staticmethod):
+                func = value.__get__(42)
+                func = func_with_new_name(func, func.__name__)
+                value = staticmethod(func)
+            elif isinstance(value, classmethod):
+                raise AssertionError("classmethods not supported "
+                                     "in 'import_from_mixin'")
+            flatten[key] = value
+    #
+    target = sys._getframe(1).f_locals
+    for key, value in flatten.items():
+        if key in target:
+            raise Exception("import_from_mixin: would overwrite the value "
+                            "already defined locally for %r" % (key,))
+        target[key] = value
diff --git a/rpython/rlib/test/test_objectmodel.py b/rpython/rlib/test/test_objectmodel.py
--- a/rpython/rlib/test/test_objectmodel.py
+++ b/rpython/rlib/test/test_objectmodel.py
@@ -548,3 +548,57 @@
 
     r = interpret(f, [29])
     assert r == 1
+
+def test_import_from_mixin():
+    class M:    # old-style
+        def f(self): pass
+    class A:    # old-style
+        import_from_mixin(M)
+    assert A.f.im_func is not M.f.im_func
+
+    class M(object):
+        def f(self): pass
+    class A:    # old-style
+        import_from_mixin(M)
+    assert A.f.im_func is not M.f.im_func
+
+    class M:    # old-style
+        def f(self): pass
+    class A(object):
+        import_from_mixin(M)
+    assert A.f.im_func is not M.f.im_func
+
+    class M(object):
+        def f(self): pass
+    class A(object):
+        import_from_mixin(M)
+    assert A.f.im_func is not M.f.im_func
+
+    class MBase(object):
+        a = 42; b = 43; c = 1000
+        def f(self): return "hi"
+        def g(self): return self.c - 1
+    class M(MBase):
+        a = 84
+        def f(self): return "there"
+    class A(object):
+        import_from_mixin(M)
+        c = 88
+    assert A.f.im_func is not M.f.im_func
+    assert A.f.im_func is not MBase.f.im_func
+    assert A.g.im_func is not MBase.g.im_func
+    assert A().f() == "there"
+    assert A.a == 84
+    assert A.b == 43
+    assert A.c == 88
+    assert A().g() == 87
+
+    try:
+        class B(object):
+            a = 63
+            import_from_mixin(M)
+    except Exception, e:
+        assert ("would overwrite the value already defined locally for 'a'"
+                in str(e))
+    else:
+        raise AssertionError("failed to detect overwritten attribute")


More information about the pypy-commit mailing list