[pypy-commit] pypy py3k: Remove unbound methods. see CPython change 48af6375207e

amauryfa noreply at buildbot.pypy.org
Thu Oct 13 01:33:42 CEST 2011


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3k
Changeset: r48006:9d18583f7fc4
Date: 2011-10-13 01:09 +0200
http://bitbucket.org/pypy/pypy/changeset/9d18583f7fc4/

Log:	Remove unbound methods. see CPython change 48af6375207e

diff --git a/pypy/interpreter/function.py b/pypy/interpreter/function.py
--- a/pypy/interpreter/function.py
+++ b/pypy/interpreter/function.py
@@ -420,87 +420,40 @@
     """functionobject.__get__(obj[, type]) -> method"""
     # this is not defined as a method on Function because it's generally
     # useful logic: w_function can be any callable.  It is used by Method too.
-    asking_for_bound = (space.is_w(w_cls, space.w_None) or
-                        not space.is_w(w_obj, space.w_None) or
-                        space.is_w(w_cls, space.type(space.w_None)))
-    if asking_for_bound:
-        return space.wrap(Method(space, w_function, w_obj, w_cls))
+    if w_obj is None or space.is_w(w_obj, space.w_None):
+        return w_function
     else:
-        return space.wrap(Method(space, w_function, None, w_cls))
+        return space.wrap(Method(space, w_function, w_obj))
 
 
 class Method(Wrappable):
-    """A method is a function bound to a specific instance or class."""
-    _immutable_fields_ = ['w_function', 'w_instance', 'w_class']
+    """A method is a function bound to a specific instance."""
+    _immutable_fields_ = ['w_function', 'w_instance']
 
-    def __init__(self, space, w_function, w_instance, w_class):
+    def __init__(self, space, w_function, w_instance):
         self.space = space
         self.w_function = w_function
-        self.w_instance = w_instance   # or None
-        self.w_class = w_class         # possibly space.w_None
+        self.w_instance = w_instance
 
-    def descr_method__new__(space, w_subtype, w_function, w_instance, w_class=None):
+    def descr_method__new__(space, w_subtype, w_function, w_instance):
         if space.is_w(w_instance, space.w_None):
             w_instance = None
-        if w_instance is None and space.is_w(w_class, space.w_None):
+        if w_instance is None:
             raise OperationError(space.w_TypeError,
-                                 space.wrap("unbound methods must have class"))
+                                 space.wrap("self must not be None"))
         method = space.allocate_instance(Method, w_subtype)
-        Method.__init__(method, space, w_function, w_instance, w_class)
+        Method.__init__(method, space, w_function, w_instance)
         return space.wrap(method)
 
     def __repr__(self):
-        if self.w_instance:
-            pre = "bound"
-        else:
-            pre = "unbound"
-        return "%s method %s" % (pre, self.w_function.getname(self.space))
+        return "bound method %s" % (self.w_function.getname(self.space),)
 
     def call_args(self, args):
         space = self.space
-        if self.w_instance is not None:
-            # bound method
-            return space.call_obj_args(self.w_function, self.w_instance, args)
-
-        # unbound method
-        w_firstarg = args.firstarg()
-        if w_firstarg is not None and (
-                space.abstract_isinstance_w(w_firstarg, self.w_class)):
-            pass  # ok
-        else:
-            myname = self.getname(space, "")
-            clsdescr = self.w_class.getname(space, "")
-            if clsdescr:
-                clsdescr += " instance"
-            else:
-                clsdescr = "instance"
-            if w_firstarg is None:
-                instdescr = "nothing"
-            else:
-                instname = space.abstract_getclass(w_firstarg).getname(space,
-                                                                       "")
-                if instname:
-                    instdescr = instname + " instance"
-                else:
-                    instdescr = "instance"
-            msg = ("unbound method %s() must be called with %s "
-                   "as first argument (got %s instead)")
-            raise operationerrfmt(space.w_TypeError, msg,
-                                  myname, clsdescr, instdescr)
-        return space.call_args(self.w_function, args)
+        return space.call_obj_args(self.w_function, self.w_instance, args)
 
     def descr_method_get(self, w_obj, w_cls=None):
-        space = self.space
-        if self.w_instance is not None:
-            return space.wrap(self)    # already bound
-        else:
-            # only allow binding to a more specific class than before
-            if (w_cls is not None and
-                not space.is_w(w_cls, space.w_None) and
-                not space.abstract_issubclass_w(w_cls, self.w_class)):
-                return space.wrap(self)    # subclass test failed
-            else:
-                return descr_function_get(space, self.w_function, w_obj, w_cls)
+        return self.space.wrap(self)    # already bound
 
     def descr_method_call(self, __args__):
         return self.call_args(__args__)
@@ -508,19 +461,11 @@
     def descr_method_repr(self):
         space = self.space
         name = self.w_function.getname(self.space)
-        # XXX do we handle all cases sanely here?
-        if space.is_w(self.w_class, space.w_None):
-            w_class = space.type(self.w_instance)
-        else:
-            w_class = self.w_class
+        w_class = space.type(self.w_instance)
         typename = w_class.getname(self.space)
-        if self.w_instance is None:
-            s = "<unbound method %s.%s>" % (typename, name)
-            return space.wrap(s)
-        else:
-            objrepr = space.str_w(space.repr(self.w_instance))
-            s = '<bound method %s.%s of %s>' % (typename, name, objrepr)
-            return space.wrap(s)
+        objrepr = space.str_w(space.repr(self.w_instance))
+        s = '<bound method %s.%s of %s>' % (typename, name, objrepr)
+        return space.wrap(s)
 
     def descr_method_getattribute(self, w_attr):
         space = self.space
@@ -539,21 +484,14 @@
         other = space.interpclass_w(w_other)
         if not isinstance(other, Method):
             return space.w_NotImplemented
-        if self.w_instance is None:
-            if other.w_instance is not None:
-                return space.w_False
-        else:
-            if other.w_instance is None:
-                return space.w_False
-            if not space.eq_w(self.w_instance, other.w_instance):
-                return space.w_False
+        if not space.eq_w(self.w_instance, other.w_instance):
+            return space.w_False
         return space.eq(self.w_function, other.w_function)
 
     def descr_method_hash(self):
         space = self.space
         w_result = space.hash(self.w_function)
-        if self.w_instance is not None:
-            w_result = space.xor(w_result, space.hash(self.w_instance))
+        w_result = space.xor(w_result, space.hash(self.w_instance))
         return w_result
 
     def descr_method__reduce__(self, space):
@@ -561,20 +499,15 @@
         from pypy.interpreter.gateway import BuiltinCode
         w_mod    = space.getbuiltinmodule('_pickle_support')
         mod      = space.interp_w(MixedModule, w_mod)
-        new_inst = mod.get('method_new')
         w        = space.wrap
         w_instance = self.w_instance or space.w_None
         function = space.interpclass_w(self.w_function)
         if isinstance(function, Function) and isinstance(function.code, BuiltinCode):
             new_inst = mod.get('builtin_method_new')
-            if space.is_w(w_instance, space.w_None):
-                tup = [self.w_class, space.wrap(function.name)]
-            else:
-                tup = [w_instance, space.wrap(function.name)]
-        elif space.is_w( self.w_class, space.w_None ):
+            tup = [w_instance, space.wrap(function.name)]
+        else:
+            new_inst = mod.get('method_new')
             tup = [self.w_function, w_instance]
-        else:
-            tup = [self.w_function, w_instance, self.w_class]
         return space.newtuple([new_inst, space.newtuple(tup)])
 
 class StaticMethod(Wrappable):
@@ -603,7 +536,7 @@
     def descr_classmethod_get(self, space, w_obj, w_klass=None):
         if space.is_w(w_klass, space.w_None):
             w_klass = space.type(w_obj)
-        return space.wrap(Method(space, self.w_function, w_klass, space.w_None))
+        return space.wrap(Method(space, self.w_function, w_klass))
 
     def descr_classmethod__new__(space, w_subtype, w_function):
         instance = space.allocate_instance(ClassMethod, w_subtype)
diff --git a/pypy/interpreter/test/test_function.py b/pypy/interpreter/test/test_function.py
--- a/pypy/interpreter/test/test_function.py
+++ b/pypy/interpreter/test/test_function.py
@@ -101,11 +101,11 @@
 
     def test_set_module_to_name_eagerly(self):
         skip("fails on PyPy but works on CPython.  Unsure we want to care")
-        exec '''if 1:
+        exec('''if 1:
             __name__ = "foo"
             def f(): pass
             __name__ = "bar"
-            assert f.__module__ == "foo"''' in {}
+            assert f.__module__ == "foo"''')
 
 
 class AppTestFunction:
@@ -295,9 +295,9 @@
 
     def test_unicode_docstring(self):
         def f():
-            u"hi"
-        assert f.__doc__ == u"hi"
-        assert type(f.__doc__) is unicode
+            "hi"
+        assert f.__doc__ == "hi"
+        assert type(f.__doc__) is str
 
     def test_subclassing(self):
         # cannot subclass 'function' or 'builtin_function'
@@ -418,13 +418,11 @@
         class A(object):
             def f(self):
                 pass
-        assert repr(A.f) == "<unbound method A.f>"
         assert repr(A().f).startswith("<bound method A.f of <")
         assert repr(A().f).endswith(">>")
         class B:
             def f(self):
                 pass
-        assert repr(B.f) == "<unbound method B.f>"
         assert repr(B().f).startswith("<bound method B.f of <")
         assert repr(A().f).endswith(">>")
 
@@ -451,69 +449,6 @@
         im = types.MethodType(A(), 3)
         assert map(im, [4]) == [7]
 
-    def test_unbound_typecheck(self):
-        class A(object):
-            def foo(self, *args):
-                return args
-        class B(A):
-            pass
-        class C(A):
-            pass
-
-        assert A.foo(A(), 42) == (42,)
-        assert A.foo(B(), 42) == (42,)
-        raises(TypeError, A.foo, 5)
-        raises(TypeError, B.foo, C())
-        try:
-            class Fun:
-                __metaclass__ = A.foo
-            assert 0  # should have raised
-        except TypeError:
-            pass
-        class Fun:
-            __metaclass__ = A().foo
-        assert Fun[:2] == ('Fun', ())
-
-    def test_unbound_abstract_typecheck(self):
-        import types
-        def f(*args):
-            return args
-        m = types.MethodType(f, None, "foobar")
-        raises(TypeError, m)
-        raises(TypeError, m, None)
-        raises(TypeError, m, "egg")
-
-        m = types.MethodType(f, None, (str, int))     # really obscure...
-        assert m(4) == (4,)
-        assert m("uh") == ("uh",)
-        raises(TypeError, m, [])
-
-        class MyBaseInst(object):
-            pass
-        class MyInst(MyBaseInst):
-            def __init__(self, myclass):
-                self.myclass = myclass
-            def __class__(self):
-                if self.myclass is None:
-                    raise AttributeError
-                return self.myclass
-            __class__ = property(__class__)
-        class MyClass(object):
-            pass
-        BBase = MyClass()
-        BSub1 = MyClass()
-        BSub2 = MyClass()
-        BBase.__bases__ = ()
-        BSub1.__bases__ = (BBase,)
-        BSub2.__bases__ = (BBase,)
-        x = MyInst(BSub1)
-        m = types.MethodType(f, None, BSub1)
-        assert m(x) == (x,)
-        raises(TypeError, m, MyInst(BBase))
-        raises(TypeError, m, MyInst(BSub2))
-        raises(TypeError, m, MyInst(None))
-        raises(TypeError, m, MyInst(42))
-
     def test_invalid_creation(self):
         import types
         def f(): pass
@@ -590,14 +525,7 @@
         # Check method returned from unbound_method.__get__()
         w_meth3 = descr_function_get(space, func, None, space.type(obj2))
         meth3 = space.unwrap(w_meth3)
-        w_meth4 = meth3.descr_method_get(obj2, space.w_None)
-        meth4 = space.unwrap(w_meth4)
-        assert isinstance(meth4, Method)
-        assert meth4.call_args(args) == obj2
-        # Check method returned from unbound_method.__get__()
-        # --- with an incompatible class
-        w_meth5 = meth3.descr_method_get(space.wrap('hello'), space.w_str)
-        assert space.is_w(w_meth5, w_meth3)
+        assert meth3 is func
 
 class TestShortcuts(object):
 
diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -109,13 +109,6 @@
     return borrow_from(w_method, w_method.w_instance)
 
 @cpython_api([PyObject], PyObject)
-def PyMethod_Class(space, w_method):
-    """Return the class object from which the method meth was created; if this was
-    created from an instance, it will be the class of the instance."""
-    assert isinstance(w_method, Method)
-    return borrow_from(w_method, w_method.w_class)
-
- at cpython_api([PyObject], PyObject)
 def PyClassMethod_New(space, w_function):
     return space.call_method(space.builtin, "classmethod", w_function)
 
diff --git a/pypy/module/cpyext/include/funcobject.h b/pypy/module/cpyext/include/funcobject.h
--- a/pypy/module/cpyext/include/funcobject.h
+++ b/pypy/module/cpyext/include/funcobject.h
@@ -16,7 +16,6 @@
 
 #define PyMethod_GET_FUNCTION(obj) PyMethod_Function((PyObject*)(obj))
 #define PyMethod_GET_SELF(obj) PyMethod_Self((PyObject*)(obj))
-#define PyMethod_GET_CLASS(obj) PyMethod_Class((PyObject*)(obj))
 
 #ifdef __cplusplus
 }


More information about the pypy-commit mailing list