summaryrefslogtreecommitdiffstats
path: root/ipalib/parameters.py
diff options
context:
space:
mode:
authorMartin Kosek <mkosek@redhat.com>2012-06-07 09:25:19 +0200
committerRob Crittenden <rcritten@redhat.com>2012-06-17 21:59:54 -0400
commit1484ccc4049dc42a5a8a71713253894ade401573 (patch)
treeaf2a2f2ed6ec5444095f74a36be13dc2ac145aa7 /ipalib/parameters.py
parent8f051c978e2a3cf40ba6cc9c84652ae049d978ab (diff)
downloadfreeipa-1484ccc4049dc42a5a8a71713253894ade401573.tar.gz
freeipa-1484ccc4049dc42a5a8a71713253894ade401573.tar.xz
freeipa-1484ccc4049dc42a5a8a71713253894ade401573.zip
Decimal parameter conversion and normalization
Parameter Decimal does not have a sufficient value checks. Some values cause Decimal parameter with a custom precision to crash with an unhandled exception. Improve parameter conversion and normalization operations to handle decimal exceptions more gracefully. Decimal parameter now also has new attributes enabling 2 new validation/normalization methods: * exponential: when False, decimal number is normalized to its non-exponential form * numberclass: a set of allowed decimal number classes (e.g. +Infinity, -Normal, ...) that are enforced for every Decimal parameter value https://fedorahosted.org/freeipa/ticket/2705
Diffstat (limited to 'ipalib/parameters.py')
-rw-r--r--ipalib/parameters.py54
1 files changed, 49 insertions, 5 deletions
diff --git a/ipalib/parameters.py b/ipalib/parameters.py
index cdc991f48..98b02dd6d 100644
--- a/ipalib/parameters.py
+++ b/ipalib/parameters.py
@@ -1206,7 +1206,12 @@ class Decimal(Number):
kwargs = Param.kwargs + (
('minvalue', decimal.Decimal, None),
('maxvalue', decimal.Decimal, None),
+ # round Decimal to given precision
('precision', int, None),
+ # when False, number is normalized to non-exponential form
+ ('exponential', bool, False),
+ # set of allowed decimal number classes
+ ('numberclass', tuple, ('-Normal', '+Zero', '+Normal')),
)
def __init__(self, name, *rules, **kw):
@@ -1256,31 +1261,70 @@ class Decimal(Number):
maxvalue=self.maxvalue,
)
+ def _enforce_numberclass(self, value):
+ #pylint: disable=E1101
+ numberclass = value.number_class()
+ if numberclass not in self.numberclass:
+ raise ValidationError(name=self.get_param_name(),
+ error=_("number class '%(cls)s' is not included in a list "
+ "of allowed number classes: %(allowed)s") \
+ % dict(cls=numberclass,
+ allowed=u', '.join(self.numberclass))
+ )
+
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)
+ try:
+ value = value.quantize(quantize_exp)
+ except decimal.DecimalException, e:
+ raise ConversionError(name=self.get_param_name(),
+ error=unicode(e))
+ return value
+
+ def _remove_exponent(self, value):
+ assert type(value) is decimal.Decimal
+ if not self.exponential: #pylint: disable=E1101
+ try:
+ # adopted from http://docs.python.org/library/decimal.html
+ value = value.quantize(decimal.Decimal(1)) \
+ if value == value.to_integral() \
+ else value.normalize()
+ except decimal.DecimalException, e:
+ raise ConversionError(name=self.get_param_name(),
+ error=unicode(e))
+
+ return value
+
+ def _test_and_normalize(self, value):
+ """
+ This method is run in conversion and normalization methods to test
+ that the Decimal number conforms to Parameter boundaries and then
+ normalizes the value.
+ """
+ self._enforce_numberclass(value)
+ value = self._remove_exponent(value)
+ value = self._enforce_precision(value)
return value
def _convert_scalar(self, value, index=None):
if isinstance(value, (basestring, float)):
try:
value = decimal.Decimal(value)
- except Exception, e:
+ except decimal.DecimalException, e:
raise ConversionError(name=self.get_param_name(), index=index,
error=unicode(e))
if isinstance(value, decimal.Decimal):
- x = self._enforce_precision(value)
- return x
+ return self._test_and_normalize(value)
return super(Decimal, self)._convert_scalar(value, index)
def _normalize_scalar(self, value):
if isinstance(value, decimal.Decimal):
- value = self._enforce_precision(value)
+ return self._test_and_normalize(value)
return super(Decimal, self)._normalize_scalar(value)