- Create a `colander.Money` type that is a Decimal type with

two-decimal-point precision rounded-up.

- Allow ``quant`` and ``rounding`` args to ``colander.Decimal`` constructor.
This commit is contained in:
Chris McDonough
2012-08-25 17:58:55 -04:00
parent 24f648dd14
commit a2dd72cde6
3 changed files with 90 additions and 8 deletions

View File

@@ -7,6 +7,11 @@ Next release
- Allow the use of 'missing=None' for Number. See
https://github.com/Pylons/colander/pull/59 .
- Create a ``colander.Money`` type that is a Decimal type with
two-decimal-point precision rounded-up.
- Allow ``quant`` and ``rounding`` args to ``colander.Decimal`` constructor.
0.9.8 (2012-04-27)
------------------

View File

@@ -1000,8 +1000,42 @@ class Float(Number):
num = float
class Decimal(Number):
""" A type representing a decimal floating point. Deserialization
returns an instance of the Python ``decimal.Decimal`` type.
"""
A type representing a decimal floating point. Deserialization returns an
instance of the Python ``decimal.Decimal`` type.
If the :attr:`colander.null` value is passed to the serialize
method of this class, the :attr:`colander.null` value will be
returned.
The Decimal constructor takes two optional arguments, ``quant`` and
``rounding``. If supplied, ``quant`` should be a string. If supplied,
``rounding`` should be one of the Python ``decimal`` module rounding
options (e.g. ``decimal.ROUND_UP``, ``decimal.ROUND_DOWN``, etc). The
serialized and deserialized result will be quantized and rounded via
``result.quantize(decimal.Decimal(quant), rounding)``. ``rounding`` is
ignored if ``quant`` is not supplied.
The subnodes of the :class:`colander.SchemaNode` that wraps
this type are ignored.
"""
def __init__(self, quant=None, rounding=None):
if quant is None:
self.quant = None
else:
self.quant = decimal.Decimal(quant)
self.rounding = rounding
def num(self, val):
result = decimal.Decimal(str(val))
if self.quant is not None:
result = result.quantize(self.quant, self.rounding)
return result
class Money(Decimal):
""" A type representing a money value with two digit precision.
Deserialization returns an instance of the Python ``decimal.Decimal``
type (quantized to two decimal places, rounded up).
If the :attr:`colander.null` value is passed to the serialize
method of this class, the :attr:`colander.null` value will be
@@ -1010,8 +1044,9 @@ class Decimal(Number):
The subnodes of the :class:`colander.SchemaNode` that wraps
this type are ignored.
"""
def num(self, val):
return decimal.Decimal(str(val))
def __init__(self):
self.quant = decimal.Decimal('.01')
self.rounding = decimal.ROUND_UP
class Boolean(SchemaType):
""" A type representing a boolean object.

View File

@@ -1249,9 +1249,9 @@ class TestFloat(unittest.TestCase):
self.assertEqual(result, '1.0')
class TestDecimal(unittest.TestCase):
def _makeOne(self):
def _makeOne(self, quant=None, rounding=None):
from colander import Decimal
return Decimal()
return Decimal(quant, rounding)
def test_serialize_null(self):
import colander
@@ -1266,8 +1266,22 @@ class TestDecimal(unittest.TestCase):
val = ''
node = DummySchemaNode(None)
typ = self._makeOne()
result = typ.deserialize(node, val)
self.assertEqual(result, colander.null)
self.assertRaises(colander.Invalid, typ.serialize, node, val)
def test_serialize_quantize_no_rounding(self):
val = '.000001'
node = DummySchemaNode(None)
typ = self._makeOne('.01')
result = typ.serialize(node, val)
self.assertEqual(result, '0.00')
def test_serialize_quantize_with_rounding_up(self):
import decimal
val = '.000001'
node = DummySchemaNode(None)
typ = self._makeOne('.01', decimal.ROUND_UP)
result = typ.serialize(node, val)
self.assertEqual(result, '0.01')
def test_deserialize_fails(self):
val = 'P'
@@ -1284,6 +1298,14 @@ class TestDecimal(unittest.TestCase):
result = typ.deserialize(node, val)
self.assertEqual(result, decimal.Decimal('1.0'))
def test_deserialize_with_quantize(self):
import decimal
val = '1.00000001'
node = DummySchemaNode(None)
typ = self._makeOne('.01', decimal.ROUND_UP)
result = typ.deserialize(node, val)
self.assertEqual(result, decimal.Decimal('1.01'))
def test_serialize_fails(self):
val = 'P'
node = DummySchemaNode(None)
@@ -1298,6 +1320,26 @@ class TestDecimal(unittest.TestCase):
result = typ.serialize(node, val)
self.assertEqual(result, '1.0')
class TestMoney(unittest.TestCase):
def _makeOne(self):
from colander import Money
return Money()
def test_serialize_rounds_up(self):
val = '1.000001'
node = DummySchemaNode(None)
typ = self._makeOne()
result = typ.serialize(node, val)
self.assertEqual(result, '1.01')
def test_deserialize_rounds_up(self):
import decimal
val = '1.00000001'
node = DummySchemaNode(None)
typ = self._makeOne()
result = typ.deserialize(node, val)
self.assertEqual(result, decimal.Decimal('1.01'))
class TestBoolean(unittest.TestCase):
def _makeOne(self):
from colander import Boolean