Commits

Antonio Cuni committed 6272fd9

bah, bug-to-bug compatibility to cpython about when it is allowed to return a
non complex from __complex__. This in part reverts 6d1ad31ec914, so it in part
reintroduce the behaviour of f1c048beb436.
See test___complex___returning_non_complex for details.

Comments (0)

Files changed (2)

pypy/objspace/std/complextype.py

 
     else:
         # non-string arguments
-        realval, imagval = unpackcomplex(space, w_real)
+        realval, imagval = unpackcomplex(space, w_real, strict_typing=False)
 
         # now take w_imag into account
         if not noarg2:
             # complex(x, y) == x+y*j, even if 'y' is already a complex.
-            realval2, imagval2 = unpackcomplex(space, w_imag)
+            realval2, imagval2 = unpackcomplex(space, w_imag, strict_typing=False)
 
             # try to preserve the signs of zeroes of realval and realval2
             if imagval2 != 0.0:
     return w_obj
 
 
-def unpackcomplex(space, w_complex):
+def unpackcomplex(space, w_complex, strict_typing=True):
+    """
+    convert w_complex into a complex and return the unwrapped (real, imag)
+    tuple. If strict_typing==True, we also typecheck the value returned by
+    __complex__ to actually be a complex (and not e.g. a float).
+    See test___complex___returning_non_complex.
+    """
     from pypy.objspace.std.complexobject import W_ComplexObject
     if type(w_complex) is W_ComplexObject:
         return (w_complex.realval, w_complex.imagval)
     if w_z is not None:
         # __complex__() must return a complex or (float,int,long) object
         # (XXX should not use isinstance here)
-        if (space.isinstance_w(w_z, space.w_int) or 
-            space.isinstance_w(w_z, space.w_long) or
-            space.isinstance_w(w_z, space.w_float)):
+        if not strict_typing and (space.isinstance_w(w_z, space.w_int) or 
+                                  space.isinstance_w(w_z, space.w_long) or
+                                  space.isinstance_w(w_z, space.w_float)):
             return (space.float_w(w_z), 0.0)
         elif isinstance(w_z, W_ComplexObject):
             return (w_z.realval, w_z.imagval)
         raise OperationError(space.w_TypeError,
                              space.wrap("__complex__() must return"
-                                        " a number"))
+                                        " a complex number"))
 
     #
     # no '__complex__' method, so we assume it is a float,

pypy/objspace/std/test/test_complexobject.py

         assert complex(OS(1+10j), 5j) == -4+10j
         assert complex(NS(1+10j), 5j) == -4+10j
 
-        assert complex(OS(2.0)) == 2+0j
-        assert complex(NS(2.0)) == 2+0j
-        assert complex(OS(2)) == 2+0j
-        assert complex(NS(2)) == 2+0j
-        assert complex(OS(2L)) == 2+0j
-        assert complex(NS(2L)) == 2+0j
-
         raises(TypeError, complex, OS(None))
         raises(TypeError, complex, NS(None))
 
         assert self.almost_equal(complex(real=float2(17.), imag=float2(23.)), 17+23j)
         raises(TypeError, complex, float2(None))
 
+    def test___complex___returning_non_complex(self):
+        import cmath
+        class Obj(object):
+            def __init__(self, value):
+                self.value = value
+            def __complex__(self):
+                return self.value
+
+        # "bug-to-bug" compatibility to CPython: complex() is more relaxed in
+        # what __complex__ can return. cmath functions really wants a complex
+        # number to be returned by __complex__.
+        assert complex(Obj(2.0)) == 2+0j
+        assert complex(Obj(2)) == 2+0j
+        assert complex(Obj(2L)) == 2+0j
+        #
+        assert cmath.polar(1) == (1.0, 0.0)
+        raises(TypeError, "cmath.polar(Obj(1))")
+        
+
     def test_hash(self):
         for x in xrange(-30, 30):
             assert hash(x) == hash(complex(x, 0))