[pypy-svn] pypy cmath: (lac, arigo)

Tue Jan 18 13:02:56 CET 2011

```Author: Armin Rigo <arigo at tunes.org>
Branch: cmath
Changeset: r40842:ef51f175776b
Date: 2011-01-18 13:02 +0100
http://bitbucket.org/pypy/pypy/changeset/ef51f175776b/

Log:	(lac, arigo)

* rewrote the logic to support complex(x, y) and
unpackcomplex(w_z).

diff --git a/pypy/module/cmath/__init__.py b/pypy/module/cmath/__init__.py
--- a/pypy/module/cmath/__init__.py
+++ b/pypy/module/cmath/__init__.py
@@ -27,6 +27,8 @@
"to polar coordinates. r is\n"
"the distance from 0 and phi the phase angle."),
'phase': "Return argument, also known as the phase angle, of a complex.",
+    'isinf': "Checks if the real or imaginary part of z is infinite.",
+    'isnan': "Checks if the real or imaginary part of z is not a number (NaN)",
}

diff --git a/pypy/objspace/std/test/test_complexobject.py b/pypy/objspace/std/test/test_complexobject.py
--- a/pypy/objspace/std/test/test_complexobject.py
+++ b/pypy/objspace/std/test/test_complexobject.py
@@ -230,10 +230,14 @@
h.raises(TypeError, complex, NS(None))
h.raises(TypeError, complex, OS(2.0))   # __complex__ must really
h.raises(TypeError, complex, NS(2.0))   # return a complex, not a float
-        h.raises((TypeError, AttributeError), complex, OS(1+10j), OS(1+10j))
-        h.raises((TypeError, AttributeError), complex, NS(1+10j), OS(1+10j))
-        h.raises((TypeError, AttributeError), complex, OS(1+10j), NS(1+10j))
-        h.raises((TypeError, AttributeError), complex, NS(1+10j), NS(1+10j))
+
+        # -- The following cases are not supported by CPython, but they
+        # -- are supported by PyPy, which is most probably ok
+        #h.raises((TypeError, AttributeError), complex, OS(1+10j), OS(1+10j))
+        #h.raises((TypeError, AttributeError), complex, NS(1+10j), OS(1+10j))
+        #h.raises((TypeError, AttributeError), complex, OS(1+10j), NS(1+10j))
+        #h.raises((TypeError, AttributeError), complex, NS(1+10j), NS(1+10j))
+
class F(object):
def __float__(self):
return 2.0

diff --git a/pypy/module/cmath/test/test_cmath.py b/pypy/module/cmath/test/test_cmath.py
--- a/pypy/module/cmath/test/test_cmath.py
+++ b/pypy/module/cmath/test/test_cmath.py
@@ -70,6 +70,44 @@
import cmath
raises(ValueError, cmath.log, 0j)

+    def test_stringarg(self):
+        import cmath
+        raises(TypeError, cmath.log, "-3j")
+
+    def test_isinf(self):
+        import cmath
+        assert not cmath.isinf(2+3j)
+        assert cmath.isinf(float("inf"))
+        assert cmath.isinf(-float("inf"))
+        assert cmath.isinf(complex("infj"))
+        assert cmath.isinf(complex("2-infj"))
+        assert cmath.isinf(complex("inf+nanj"))
+        assert cmath.isinf(complex("nan+infj"))
+
+    def test_isnan(self):
+        import cmath
+        assert not cmath.isnan(2+3j)
+        assert cmath.isnan(float("nan"))
+        assert cmath.isnan(complex("nanj"))
+        assert cmath.isnan(complex("inf+nanj"))
+        assert cmath.isnan(complex("nan+infj"))
+
+    def test_user_defined_complex(self):
+        import cmath
+        class Foo(object):
+            def __complex__(self):
+                return 2j
+        r, phi = cmath.polar(Foo())
+        assert r == 2
+        assert abs(phi - cmath.pi/2) < 1e-10
+
+    def test_user_defined_float(self):
+        import cmath
+        class Foo(object):
+            def __float__(self):
+                return 2.0
+        assert cmath.polar(Foo()) == (2, 0)
+

def parse_testfile(fname):
"""Parse a file with test values

diff --git a/pypy/module/cmath/interp_cmath.py b/pypy/module/cmath/interp_cmath.py
--- a/pypy/module/cmath/interp_cmath.py
+++ b/pypy/module/cmath/interp_cmath.py
@@ -564,3 +564,25 @@
return space.newtuple([space.newfloat(resx), space.newfloat(resy)])
wrapped_polar.unwrap_spec = [ObjSpace, W_Root]
wrapped_polar.func_doc = names_and_docstrings['polar']
+
+
+def c_isinf(x, y):
+    return isinf(x) or isinf(y)
+
+def wrapped_isinf(space, w_z):
+    x, y = space.unpackcomplex(w_z)
+    res = c_isinf(x, y)
+    return space.newbool(res)
+wrapped_isinf.unwrap_spec = [ObjSpace, W_Root]
+wrapped_isinf.func_doc = names_and_docstrings['isinf']
+
+
+def c_isnan(x, y):
+    return isnan(x) or isnan(y)
+
+def wrapped_isnan(space, w_z):
+    x, y = space.unpackcomplex(w_z)
+    res = c_isnan(x, y)
+    return space.newbool(res)
+wrapped_isnan.unwrap_spec = [ObjSpace, W_Root]
+wrapped_isnan.func_doc = names_and_docstrings['isnan']

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
@@ -265,18 +265,8 @@
return W_ComplexObject(realval, imagval)

def unpackcomplex(self, w_complex):
-        if isinstance(w_complex, W_ComplexObject):
-            return (w_complex.realval, w_complex.imagval)
-        else:
-            try:
-                floatval = self.float_w(w_complex)
-            except OperationError, e:
-                if not e.match(self, self.w_TypeError):
-                    raise
-                raise operationerrfmt(self.w_TypeError,
-                                      "complex number expected, got '%s'",
-                                      self.type(w_complex).getname(self))
-            return (floatval, 0.0)
+        from pypy.objspace.std.complextype import unpackcomplex
+        return unpackcomplex(self, w_complex)

def newlong(self, val): # val is an int
return W_LongObject.fromint(self, val)

diff --git a/pypy/objspace/std/complextype.py b/pypy/objspace/std/complextype.py
--- a/pypy/objspace/std/complextype.py
+++ b/pypy/objspace/std/complextype.py
@@ -1,5 +1,5 @@
from pypy.interpreter import gateway
-from pypy.interpreter.error import OperationError
+from pypy.interpreter.error import OperationError, operationerrfmt
from pypy.objspace.std.register_all import register_all
from pypy.objspace.std.strutil import string_to_float, ParseStringError
from pypy.objspace.std.noneobject import W_NoneObject
@@ -139,62 +139,63 @@

else:
# non-string arguments
-
-        # test for a '__complex__' method, and call it if found.
-        # A bit of a hack to support old-style classes: don't use
-        # space.lookup() (this is similar to CPython).
-        try:
-            w_method = space.getattr(w_real, space.wrap('__complex__'))
-        except OperationError, e:
-            if not e.match(space, space.w_AttributeError):
-                raise
-        else:
-            w_real = space.call_function(w_method)
-            # __complex__() must return a complex object
-            if not space.is_true(space.isinstance(w_real, space.w_complex)):
-                raise OperationError(space.w_TypeError,
-                                     space.wrap("__complex__() must return"
-                                                " a complex number"))
-
-        # at this point w_real can be an instance of 'complex',
-        # either because it is the result of __complex__() or because
-        # the shortcut at the beginning of the function didn't match
-        if space.is_true(space.isinstance(w_real, space.w_complex)):
-            # note that we are unwrapping the complex for the rest of
-            # the code.  This also ensures that we eventually return
-            # an object of the correct subclass of complex.
-            realval = space.float_w(space.getattr(w_real, space.wrap('real')))
-            imagval = space.float_w(space.getattr(w_real, space.wrap('imag')))
-        else:
-            realval = space.float_w(space.float(w_real))
-            imagval = 0.0
+        realval, imagval = unpackcomplex(space, w_real)

# now take w_imag into account
if not noarg2:
-            if space.is_true(space.isinstance(w_imag, space.w_complex)):
-                # complex(x, y) == x+y*j, even if 'y' is already a complex.
-                # say y == a+b*j:
-                a = space.float_w(space.getattr(w_imag, space.wrap('real')))
-                b = space.float_w(space.getattr(w_imag, space.wrap('imag')))
-                realval -= b
-                imagval += a
-            elif space.is_true(space.isinstance(w_imag, space.w_str)) or \
-                     space.is_true(space.isinstance(w_imag, space.w_unicode)):
-                # prevent space.float(w_imag) from succeeding
-                raise OperationError(space.w_TypeError,
-                                     space.wrap("complex() second arg"
-                                                " can't be a string"))
+            # complex(x, y) == x+y*j, even if 'y' is already a complex.
+            realval2, imagval2 = unpackcomplex(space, w_imag)
+
+            # try to preserve the signs of zeroes of realval and realval2
+            if imagval2 != 0.0:
+                realval -= imagval2
+
+            if imagval != 0.0:
+                imagval += realval2
else:
-                a = space.float_w(space.float(w_imag))
-                if imagval != 0.0:
-                    imagval += a
-                else:
-                    imagval = a
+                imagval = realval2
# done
w_obj = space.allocate_instance(W_ComplexObject, w_complextype)
W_ComplexObject.__init__(w_obj, realval, imagval)
return w_obj

+
+def unpackcomplex(space, w_complex):
+    from pypy.objspace.std.complexobject import W_ComplexObject
+    if type(w_complex) is W_ComplexObject:
+        return (w_complex.realval, w_complex.imagval)
+    #
+    # test for a '__complex__' method, and call it if found.
+    # A bit of a hack to support old-style classes: don't use
+    # space.lookup() (this is similar to CPython).
+    try:
+        w_method = space.getattr(w_complex, space.wrap('__complex__'))
+    except OperationError, e:
+        if not e.match(space, space.w_AttributeError):
+            raise
+        if isinstance(w_complex, W_ComplexObject):
+            return (w_complex.realval, w_complex.imagval)
+    else:
+        w_z = space.call_function(w_method)
+        # __complex__() must return a complex object
+        # (XXX should not use isinstance here)
+        if not isinstance(w_z, W_ComplexObject):
+            raise OperationError(space.w_TypeError,
+                                 space.wrap("__complex__() must return"
+                                            " a complex number"))
+        return (w_z.realval, w_z.imagval)
+    #
+    # no '__complex__' method, so we assume it is a float.
+    # Check that it is not a string (on which space.float() would succeed).
+    if (space.is_true(space.isinstance(w_complex, space.w_str)) or
+        space.is_true(space.isinstance(w_complex, space.w_unicode))):
+        raise operationerrfmt(space.w_TypeError,
+                              "complex number expected, got '%s'",
+                              space.type(w_complex).getname(space))
+    #
+    return (space.float_w(space.float(w_complex)), 0.0)
+
+
def complexwprop(name):
def fget(space, w_obj):
from pypy.objspace.std.complexobject import W_ComplexObject
```