Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit