Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r79925:fed018f3c786
Date: 2015-10-02 09:59 +0200
http://bitbucket.org/pypy/pypy/changeset/fed018f3c786/

Log:    Issue #2137: try harder to avoid infinite recursion in some cases of
        __coerce__() with old-style instances

diff --git a/pypy/module/__builtin__/interp_classobj.py 
b/pypy/module/__builtin__/interp_classobj.py
--- a/pypy/module/__builtin__/interp_classobj.py
+++ b/pypy/module/__builtin__/interp_classobj.py
@@ -253,27 +253,30 @@
 
     def binaryop(self, space, w_other):
         w_a, w_b = _coerce_helper(space, self, w_other)
-        if w_a is None:
-            w_a = self
-            w_b = w_other
-        if w_a is self:
-            w_meth = self.getattr(space, specialname, False)
+        if isinstance(w_a, W_InstanceObject):
+            w_meth = w_a.getattr(space, specialname, False)
             if w_meth is None:
                 return space.w_NotImplemented
             return space.call_function(w_meth, w_b)
         else:
+            # fall back to space.xxx() if coerce returns a non-W_Instance
+            # object as first argument
             return getattr(space, objspacename)(w_a, w_b)
     binaryop.func_name = name
 
     def rbinaryop(self, space, w_other):
         w_a, w_b = _coerce_helper(space, self, w_other)
-        if w_a is None or w_a is self:
-            w_meth = self.getattr(space, rspecialname, False)
+        if isinstance(w_a, W_InstanceObject):
+            w_meth = w_a.getattr(space, rspecialname, False)
             if w_meth is None:
                 return space.w_NotImplemented
-            return space.call_function(w_meth, w_other)
+            return space.call_function(w_meth, w_b)
         else:
-            return getattr(space, objspacename)(w_b, w_a)
+            # here, if coerce returns a non-W_Instance object as first
+            # argument, then give up.  The idea is that this strange
+            # case should already have been handled by the binaryop()
+            # called from descroperation first.
+            return space.w_NotImplemented
     rbinaryop.func_name = "r" + name
     return binaryop, rbinaryop
 
@@ -283,7 +286,7 @@
     except OperationError, e:
         if not e.match(space, space.w_TypeError):
             raise
-        return [None, None]
+        return [w_self, w_other]
     return space.fixedview(w_tup, 2)
 
 def descr_instance_new(space, w_type, w_class, w_dict=None):
@@ -523,13 +526,9 @@
 
     def descr_cmp(self, space, w_other): # do all the work here like CPython
         w_a, w_b = _coerce_helper(space, self, w_other)
-        if w_a is None:
-            w_a = self
-            w_b = w_other
-        else:
-            if (not isinstance(w_a, W_InstanceObject) and
-                not isinstance(w_b, W_InstanceObject)):
-                return space.cmp(w_a, w_b)
+        if (not isinstance(w_a, W_InstanceObject) and
+            not isinstance(w_b, W_InstanceObject)):
+            return space.cmp(w_a, w_b)
         if isinstance(w_a, W_InstanceObject):
             w_func = w_a.getattr(space, '__cmp__', False)
             if w_func is not None:
@@ -636,42 +635,36 @@
     def descr_pow(self, space, w_other, w_modulo=None):
         if space.is_none(w_modulo):
             w_a, w_b = _coerce_helper(space, self, w_other)
-            if w_a is None:
-                w_a = self
-                w_b = w_other
-            if w_a is self:
-                w_func = self.getattr(space, '__pow__', False)
-                if w_func is not None:
-                    return space.call_function(w_func, w_other)
-                return space.w_NotImplemented
+            if isinstance(w_a, W_InstanceObject):
+                w_func = w_a.getattr(space, '__pow__', False)
+                if w_func is None:
+                    return space.w_NotImplemented
+                return space.call_function(w_func, w_other)
             else:
                 return space.pow(w_a, w_b, space.w_None)
         else:
             # CPython also doesn't try coercion in this case
             w_func = self.getattr(space, '__pow__', False)
-            if w_func is not None:
-                return space.call_function(w_func, w_other, w_modulo)
-            return space.w_NotImplemented
+            if w_func is None:
+                return space.w_NotImplemented
+            return space.call_function(w_func, w_other, w_modulo)
 
     def descr_rpow(self, space, w_other, w_modulo=None):
         if space.is_none(w_modulo):
             w_a, w_b = _coerce_helper(space, self, w_other)
-            if w_a is None:
-                w_a = self
-                w_b = w_other
-            if w_a is self:
-                w_func = self.getattr(space, '__rpow__', False)
-                if w_func is not None:
-                    return space.call_function(w_func, w_other)
+            if isinstance(w_a, W_InstanceObject):
+                w_func = w_a.getattr(space, '__rpow__', False)
+                if w_func is None:
+                    return space.w_NotImplemented
+                return space.call_function(w_func, w_other)
+            else:
                 return space.w_NotImplemented
-            else:
-                return space.pow(w_b, w_a, space.w_None)
         else:
             # CPython also doesn't try coercion in this case
             w_func = self.getattr(space, '__rpow__', False)
-            if w_func is not None:
-                return space.call_function(w_func, w_other, w_modulo)
-            return space.w_NotImplemented
+            if w_func is None:
+                return space.w_NotImplemented
+            return space.call_function(w_func, w_other, w_modulo)
 
     def descr_next(self, space):
         w_func = self.getattr(space, 'next', False)
diff --git a/pypy/module/__builtin__/test/test_classobj.py 
b/pypy/module/__builtin__/test/test_classobj.py
--- a/pypy/module/__builtin__/test/test_classobj.py
+++ b/pypy/module/__builtin__/test/test_classobj.py
@@ -417,6 +417,14 @@
             pass
         raises(TypeError, coerce, B(), [])
 
+    def test_coerce_inf(self):
+        class B:
+            def __coerce__(self, other):
+                return B(), B()
+            def __add__(self, other):
+                return 42
+        assert B() + B() == 42
+
     def test_binaryop(self):
         class A:
             def __add__(self, other):
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to