Author: Amaury Forgeot d'Arc <amaur...@gmail.com>
Branch: decimal-libmpdec
Changeset: r71455:c80a6ebb000b
Date: 2014-05-06 22:00 +0200
http://bitbucket.org/pypy/pypy/changeset/c80a6ebb000b/

Log:    Add tuple->Decimal conversion

diff --git a/pypy/module/_decimal/interp_decimal.py 
b/pypy/module/_decimal/interp_decimal.py
--- a/pypy/module/_decimal/interp_decimal.py
+++ b/pypy/module/_decimal/interp_decimal.py
@@ -1,4 +1,5 @@
 from rpython.rlib import rmpdec, rarithmetic, rbigint
+from rpython.rlib.rstring import StringBuilder
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.interpreter.baseobjspace import W_Root
 from pypy.interpreter.error import oefmt, OperationError
@@ -118,6 +119,82 @@
             raise ValueError("Bad rbigint size")
     return w_result
 
+def decimal_from_tuple(space, w_subtype, w_value, context, exact=True):
+    w_sign, w_digits, w_exponent  = space.unpackiterable(w_value, 3)
+
+    # Make a string representation of a DecimalTuple
+    builder = StringBuilder(20)
+
+    # sign
+    try:
+        sign = space.int_w(w_sign)
+    except OperationError as e:
+        if not e.match(space, space.w_TypeError):
+            raise
+        sign = -1
+    if sign != 0 and sign != 1:
+        raise oefmt(space.w_ValueError,
+                    "sign must be an integer with the value 0 or 1")
+    builder.append('-' if sign else '+')
+
+    # exponent or encoding for a special number
+    is_infinite = False
+    is_special = False
+    exponent = 0
+    if space.isinstance_w(w_exponent, space.w_unicode):
+        # special
+        is_special = True
+        val = space.unicode_w(w_exponent)
+        if val == 'F':
+            builder.append('Inf')
+            is_infinite = True
+        elif val == 'n':
+            builder.append('Nan')
+        elif val == 'N':
+            builder.append('sNan')
+        else:
+            raise oefmt(space.w_ValueError,
+                        "string argument in the third position "
+                        "must be 'F', 'n' or 'N'")
+    else:
+        # exponent
+        try:
+            exponent = space.int_w(w_exponent)
+        except OperationError as e:
+            if not e.match(space, space.w_TypeError):
+                raise
+            raise oefmt(space.w_ValueError,
+                        "exponent must be an integer")            
+
+    # coefficient
+    digits_w = space.unpackiterable(w_digits)
+
+    if not digits_w and not is_special:
+        # empty tuple: zero coefficient, except for special numbers
+        strval += '0'
+    for w_digit in digits_w:
+        try:
+            digit = space.int_w(w_digit)
+        except OperationError as e:
+            if not e.match(space, space.w_TypeError):
+                raise
+            digit = -1
+        if not 0 <= digit <= 9:
+            raise oefmt(space.w_ValueError,
+                        "coefficient must be a tuple of digits")
+        if is_infinite:
+            # accept but ignore any well-formed coefficient for
+            # compatibility with decimal.py
+            continue
+        builder.append(chr(ord('0') + digit))
+            
+    if not is_special:
+        builder.append('E')
+        builder.append(str(exponent))
+
+    strval = builder.build()
+    return decimal_from_cstring(space, w_subtype, strval, context, exact=exact)
+
 def decimal_from_object(space, w_subtype, w_value, context, exact=True):
     if w_value is None:
         return decimal_from_ssize(space, w_subtype, 0, context, exact=exact)
@@ -127,6 +204,10 @@
     elif space.isinstance_w(w_value, space.w_int):
         return decimal_from_long(space, w_subtype, w_value, context,
                                  exact=exact)
+    elif (space.isinstance_w(w_value, space.w_list) or
+          space.isinstance_w(w_value, space.w_tuple)):
+        return decimal_from_tuple(space, w_subtype, w_value, context,
+                                 exact=exact)
     raise oefmt(space.w_TypeError,
                 "conversion from %N to Decimal is not supported",
                 space.type(w_value))
diff --git a/pypy/module/_decimal/test/test_decimal.py 
b/pypy/module/_decimal/test/test_decimal.py
--- a/pypy/module/_decimal/test/test_decimal.py
+++ b/pypy/module/_decimal/test/test_decimal.py
@@ -90,3 +90,61 @@
             # embedded NUL
             raises(InvalidOperation, Decimal, "12\u00003")
 
+    def test_explicit_from_tuples(self):
+        Decimal = self.decimal.Decimal
+
+        #zero
+        d = Decimal( (0, (0,), 0) )
+        assert str(d) == '0'
+
+        #int
+        d = Decimal( (1, (4, 5), 0) )
+        assert str(d) == '-45'
+
+        #float
+        d = Decimal( (0, (4, 5, 3, 4), -2) )
+        assert str(d) == '45.34'
+
+        #weird
+        d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
+        assert str(d) == '-4.34913534E-17'
+
+        #inf
+        d = Decimal( (0, (), "F") )
+        assert str(d) == 'Infinity'
+
+        #wrong number of items
+        raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
+
+        #bad sign
+        raises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
+        raises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) )
+        raises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2))
+
+        #bad exp
+        raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
+        raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) )
+        raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
+
+        #bad coefficients
+        raises(ValueError, Decimal, (1, "xyz", 2) )
+        raises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
+        raises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
+        raises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
+        raises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) )
+
+    def test_explicit_from_list(self):
+        Decimal = self.decimal.Decimal
+
+        d = Decimal([0, [0], 0])
+        assert str(d) == '0'
+
+        d = Decimal([1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25])
+        assert str(d) == '-4.34913534E-17'
+
+        d = Decimal([1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25])
+        assert str(d) == '-4.34913534E-17'
+
+        d = Decimal((1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25))
+        assert str(d) == '-4.34913534E-17'
+
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to