summaryrefslogtreecommitdiffstats
path: root/ipalib/parameters.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib/parameters.py')
-rw-r--r--ipalib/parameters.py87
1 files changed, 69 insertions, 18 deletions
diff --git a/ipalib/parameters.py b/ipalib/parameters.py
index be210864f..d918a5737 100644
--- a/ipalib/parameters.py
+++ b/ipalib/parameters.py
@@ -100,6 +100,7 @@ a more detailed description for clarity.
"""
import re
+import decimal
from types import NoneType
from util import make_repr
from text import _ as ugettext
@@ -723,8 +724,6 @@ class Param(ReadOnly):
else:
newval += (v,)
value = newval
- if self.normalizer is None:
- return value
if self.multivalue:
return tuple(
self._normalize_scalar(v) for v in value
@@ -740,6 +739,8 @@ class Param(ReadOnly):
"""
if type(value) is not unicode:
return value
+ if self.normalizer is None:
+ return value
try:
return self.normalizer(value)
except StandardError:
@@ -1100,7 +1101,7 @@ class Flag(Bool):
class Number(Param):
"""
- Base class for the `Int` and `Float` parameters.
+ Base class for the `Int` and `Decimal` parameters.
"""
def _convert_scalar(self, value, index=None):
@@ -1225,36 +1226,59 @@ class Int(Number):
)
-class Float(Number):
+class Decimal(Number):
"""
- A parameter for floating-point values (stored in the ``float`` type).
+ A parameter for floating-point values (stored in the ``Decimal`` type).
+
+ Python Decimal type helps overcome problems tied to plain "float" type,
+ e.g. problem with representation or value comparison. In order to safely
+ transfer the value over RPC libraries, it is being converted to string
+ which is then converted back to Decimal number.
"""
- type = float
+ type = decimal.Decimal
type_error = _('must be a decimal number')
kwargs = Param.kwargs + (
- ('minvalue', float, None),
- ('maxvalue', float, None),
+ ('minvalue', decimal.Decimal, None),
+ ('maxvalue', decimal.Decimal, None),
+ ('precision', int, None),
)
def __init__(self, name, *rules, **kw):
- #pylint: disable=E1003
- super(Number, self).__init__(name, *rules, **kw)
-
- if (self.minvalue > self.maxvalue) and (self.minvalue is not None and self.maxvalue is not None):
+ for kwparam in ('minvalue', 'maxvalue', 'default'):
+ value = kw.get(kwparam)
+ if value is None:
+ continue
+ if isinstance(value, (basestring, float)):
+ try:
+ value = decimal.Decimal(value)
+ except Exception, e:
+ raise ValueError(
+ '%s: cannot parse kwarg %s: %s' % (
+ name, kwparam, str(e)))
+ kw[kwparam] = value
+
+ super(Decimal, self).__init__(name, *rules, **kw)
+
+ if (self.minvalue > self.maxvalue) \
+ and (self.minvalue is not None and \
+ self.maxvalue is not None):
raise ValueError(
- '%s: minvalue > maxvalue (minvalue=%r, maxvalue=%r)' % (
+ '%s: minvalue > maxvalue (minvalue=%s, maxvalue=%s)' % (
self.nice, self.minvalue, self.maxvalue)
)
+ if self.precision is not None and self.precision < 0:
+ raise ValueError('%s: precision must be at least 0' % self.nice)
+
def _rule_minvalue(self, _, value):
"""
Check min constraint.
"""
- assert type(value) is float
+ assert type(value) is decimal.Decimal
if value < self.minvalue:
- return _('must be at least %(minvalue)f') % dict(
+ return _('must be at least %(minvalue)s') % dict(
minvalue=self.minvalue,
)
@@ -1262,12 +1286,39 @@ class Float(Number):
"""
Check max constraint.
"""
- assert type(value) is float
+ assert type(value) is decimal.Decimal
if value > self.maxvalue:
- return _('can be at most %(maxvalue)f') % dict(
+ return _('can be at most %(maxvalue)s') % dict(
maxvalue=self.maxvalue,
)
+ def _enforce_precision(self, value):
+ assert type(value) is decimal.Decimal
+ if self.precision is not None:
+ quantize_exp = decimal.Decimal(10) ** -self.precision
+ return value.quantize(quantize_exp)
+
+ return value
+
+ def _convert_scalar(self, value, index=None):
+ if isinstance(value, (basestring, float)):
+ try:
+ value = decimal.Decimal(value)
+ except Exception, e:
+ raise ConversionError(name=self.name, index=index,
+ error=unicode(e))
+
+ if isinstance(value, decimal.Decimal):
+ x = self._enforce_precision(value)
+ return x
+
+ return super(Decimal, self)._convert_scalar(value, index)
+
+ def _normalize_scalar(self, value):
+ if isinstance(value, decimal.Decimal):
+ value = self._enforce_precision(value)
+
+ return super(Decimal, self)._normalize_scalar(value)
class Data(Param):
"""
@@ -1423,7 +1474,7 @@ class Str(Data):
"""
if type(value) is self.type:
return value
- if type(value) in (int, float):
+ if type(value) in (int, float, decimal.Decimal):
return self.type(value)
if type(value) in (tuple, list):
raise ConversionError(name=self.name, index=index,