[pypy-commit] pypy py3k: Fix a bug that caused e.g. None.__eq__ to evaluate to an unbound function.

mjacob noreply at buildbot.pypy.org
Fri Jun 12 05:39:33 CEST 2015


Author: Manuel Jacob <me at manueljacob.de>
Branch: py3k
Changeset: r78038:36f6f45e781a
Date: 2015-06-12 05:39 +0200
http://bitbucket.org/pypy/pypy/changeset/36f6f45e781a/

Log:	Fix a bug that caused e.g. None.__eq__ to evaluate to an unbound
	function.

diff --git a/pypy/module/__builtin__/test/test_builtin.py b/pypy/module/__builtin__/test/test_builtin.py
--- a/pypy/module/__builtin__/test/test_builtin.py
+++ b/pypy/module/__builtin__/test/test_builtin.py
@@ -839,6 +839,22 @@
         raises(TypeError, setattr, A(), 42, 'x')
         raises(TypeError, delattr, A(), 42)
 
+    def test_getattr_None(self):
+        from types import FunctionType, MethodType
+        assert isinstance(getattr(type(None), '__eq__'), FunctionType)
+        assert isinstance(getattr(None, '__eq__'), MethodType)
+
+    def test_getattr_userobject(self):
+        from types import FunctionType, MethodType
+        class A(object):
+            def __eq__(self, other):
+                pass
+        a = A()
+        assert isinstance(getattr(A, '__eq__'), FunctionType)
+        assert isinstance(getattr(a, '__eq__'), MethodType)
+        a.__eq__ = 42
+        assert a.__eq__ == 42
+
 
 class AppTestGetattrWithGetAttributeShortcut(AppTestGetattr):
     spaceconfig = {"objspace.std.getattributeshortcut": True}
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -102,6 +102,13 @@
         if w_value is not None:
             return w_value
         if w_descr is not None:
+            typ = type(w_descr)
+            if typ is Function or typ is FunctionWithFixedCode:
+                # This shortcut is necessary if w_obj is None.  Otherwise e.g.
+                # None.__eq__ would return an unbound function because calling
+                # __get__ with None as the first argument returns the attribute
+                # as if it was accessed through the owner (type(None).__eq__).
+                return Method(space, w_descr, w_obj)
             return space.get(w_descr, w_obj)
         raiseattrerror(space, w_obj, w_name)
 
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -2,6 +2,7 @@
 from pypy.interpreter import special
 from pypy.interpreter.baseobjspace import ObjSpace, W_Root
 from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.function import Function, Method, FunctionWithFixedCode
 from pypy.interpreter.typedef import get_unique_interplevel_subclass
 from pypy.objspace.std import frame, transparent, callmethod
 from pypy.objspace.descroperation import (
@@ -572,6 +573,13 @@
                     return w_value
                 if not is_data:
                     w_get = self.lookup(w_descr, "__get__")
+            typ = type(w_descr)
+            if typ is Function or typ is FunctionWithFixedCode:
+                # This shortcut is necessary if w_obj is None.  Otherwise e.g.
+                # None.__eq__ would return an unbound function because calling
+                # __get__ with None as the first argument returns the attribute
+                # as if it was accessed through the owner (type(None).__eq__).
+                return Method(self, w_descr, w_obj)
             if w_get is not None:
                 # __get__ is allowed to raise an AttributeError to trigger
                 # use of __getattr__.


More information about the pypy-commit mailing list