[pypy-svn] pypy cmath: (lac, arigo)
arigo
commits-noreply at bitbucket.org
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)
* add cmath.isinf() and cmath.isnan().
* 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
More information about the Pypy-commit
mailing list