summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt36
-rw-r--r--doc/guide/guide.org8
-rw-r--r--ipalib/__init__.py2
-rw-r--r--ipalib/encoder.py6
-rw-r--r--ipalib/parameters.py87
-rw-r--r--ipalib/plugins/dns.py51
-rw-r--r--ipalib/rpc.py4
-rw-r--r--ipaserver/rpcserver.py3
-rwxr-xr-xmake-lint2
-rw-r--r--tests/test_ipalib/test_parameters.py47
-rw-r--r--tests/test_xmlrpc/test_dns_plugin.py2
11 files changed, 160 insertions, 88 deletions
diff --git a/API.txt b/API.txt
index 9048231bb..2937c24f4 100644
--- a/API.txt
+++ b/API.txt
@@ -654,16 +654,16 @@ option: Str('kx_part_exchanger', attribute=False, cli_name='kx_exchanger', multi
option: LOCRecord('locrecord', attribute=True, cli_name='loc_rec', csv=True, multivalue=True, option_group=u'LOC Record', required=False)
option: Int('loc_part_lat_deg', attribute=False, cli_name='loc_lat_deg', maxvalue=90, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
option: Int('loc_part_lat_min', attribute=False, cli_name='loc_lat_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_lat_sec', attribute=False, cli_name='loc_lat_sec', maxvalue=59.999, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
+option: Decimal('loc_part_lat_sec', attribute=False, cli_name='loc_lat_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, required=False)
option: StrEnum('loc_part_lat_dir', attribute=False, cli_name='loc_lat_dir', multivalue=False, option_group=u'LOC Record', required=False, values=(u'N', u'S'))
option: Int('loc_part_lon_deg', attribute=False, cli_name='loc_lon_deg', maxvalue=180, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
option: Int('loc_part_lon_min', attribute=False, cli_name='loc_lon_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_lon_sec', attribute=False, cli_name='loc_lon_sec', maxvalue=59.999, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
+option: Decimal('loc_part_lon_sec', attribute=False, cli_name='loc_lon_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, required=False)
option: StrEnum('loc_part_lon_dir', attribute=False, cli_name='loc_lon_dir', multivalue=False, option_group=u'LOC Record', required=False, values=(u'E', u'W'))
-option: Float('loc_part_altitude', attribute=False, cli_name='loc_altitude', maxvalue=42849672.95, minvalue=-100000.0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_size', attribute=False, cli_name='loc_size', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_h_precision', attribute=False, cli_name='loc_h_precision', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_v_precision', attribute=False, cli_name='loc_v_precision', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
+option: Decimal('loc_part_altitude', attribute=False, cli_name='loc_altitude', maxvalue=Decimal('42849672.95'), minvalue=Decimal('-100000.00'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
+option: Decimal('loc_part_size', attribute=False, cli_name='loc_size', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
+option: Decimal('loc_part_h_precision', attribute=False, cli_name='loc_h_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
+option: Decimal('loc_part_v_precision', attribute=False, cli_name='loc_v_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
option: MXRecord('mxrecord', attribute=True, cli_name='mx_rec', csv=True, multivalue=True, option_group=u'MX Record', required=False)
option: Int('mx_part_preference', attribute=False, cli_name='mx_preference', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'MX Record', required=False)
option: Str('mx_part_exchanger', attribute=False, cli_name='mx_exchanger', multivalue=False, option_group=u'MX Record', required=False)
@@ -831,16 +831,16 @@ option: Str('kx_part_exchanger', attribute=False, autofill=False, cli_name='kx_e
option: LOCRecord('locrecord', attribute=True, autofill=False, cli_name='loc_rec', csv=True, multivalue=True, option_group=u'LOC Record', query=True, required=False)
option: Int('loc_part_lat_deg', attribute=False, autofill=False, cli_name='loc_lat_deg', maxvalue=90, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
option: Int('loc_part_lat_min', attribute=False, autofill=False, cli_name='loc_lat_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
-option: Float('loc_part_lat_sec', attribute=False, autofill=False, cli_name='loc_lat_sec', maxvalue=59.999, minvalue=0.0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
+option: Decimal('loc_part_lat_sec', attribute=False, autofill=False, cli_name='loc_lat_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, query=True, required=False)
option: StrEnum('loc_part_lat_dir', attribute=False, autofill=False, cli_name='loc_lat_dir', multivalue=False, option_group=u'LOC Record', query=True, required=False, values=(u'N', u'S'))
option: Int('loc_part_lon_deg', attribute=False, autofill=False, cli_name='loc_lon_deg', maxvalue=180, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
option: Int('loc_part_lon_min', attribute=False, autofill=False, cli_name='loc_lon_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
-option: Float('loc_part_lon_sec', attribute=False, autofill=False, cli_name='loc_lon_sec', maxvalue=59.999, minvalue=0.0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
+option: Decimal('loc_part_lon_sec', attribute=False, autofill=False, cli_name='loc_lon_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, query=True, required=False)
option: StrEnum('loc_part_lon_dir', attribute=False, autofill=False, cli_name='loc_lon_dir', multivalue=False, option_group=u'LOC Record', query=True, required=False, values=(u'E', u'W'))
-option: Float('loc_part_altitude', attribute=False, autofill=False, cli_name='loc_altitude', maxvalue=42849672.95, minvalue=-100000.0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
-option: Float('loc_part_size', attribute=False, autofill=False, cli_name='loc_size', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
-option: Float('loc_part_h_precision', attribute=False, autofill=False, cli_name='loc_h_precision', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
-option: Float('loc_part_v_precision', attribute=False, autofill=False, cli_name='loc_v_precision', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', query=True, required=False)
+option: Decimal('loc_part_altitude', attribute=False, autofill=False, cli_name='loc_altitude', maxvalue=Decimal('42849672.95'), minvalue=Decimal('-100000.00'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False)
+option: Decimal('loc_part_size', attribute=False, autofill=False, cli_name='loc_size', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False)
+option: Decimal('loc_part_h_precision', attribute=False, autofill=False, cli_name='loc_h_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False)
+option: Decimal('loc_part_v_precision', attribute=False, autofill=False, cli_name='loc_v_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, query=True, required=False)
option: MXRecord('mxrecord', attribute=True, autofill=False, cli_name='mx_rec', csv=True, multivalue=True, option_group=u'MX Record', query=True, required=False)
option: Int('mx_part_preference', attribute=False, autofill=False, cli_name='mx_preference', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'MX Record', query=True, required=False)
option: Str('mx_part_exchanger', attribute=False, autofill=False, cli_name='mx_exchanger', multivalue=False, option_group=u'MX Record', query=True, required=False)
@@ -952,16 +952,16 @@ option: Str('kx_part_exchanger', attribute=False, autofill=False, cli_name='kx_e
option: LOCRecord('locrecord', attribute=True, autofill=False, cli_name='loc_rec', csv=True, multivalue=True, option_group=u'LOC Record', required=False)
option: Int('loc_part_lat_deg', attribute=False, autofill=False, cli_name='loc_lat_deg', maxvalue=90, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
option: Int('loc_part_lat_min', attribute=False, autofill=False, cli_name='loc_lat_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_lat_sec', attribute=False, autofill=False, cli_name='loc_lat_sec', maxvalue=59.999, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
+option: Decimal('loc_part_lat_sec', attribute=False, autofill=False, cli_name='loc_lat_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, required=False)
option: StrEnum('loc_part_lat_dir', attribute=False, autofill=False, cli_name='loc_lat_dir', multivalue=False, option_group=u'LOC Record', required=False, values=(u'N', u'S'))
option: Int('loc_part_lon_deg', attribute=False, autofill=False, cli_name='loc_lon_deg', maxvalue=180, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
option: Int('loc_part_lon_min', attribute=False, autofill=False, cli_name='loc_lon_min', maxvalue=59, minvalue=0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_lon_sec', attribute=False, autofill=False, cli_name='loc_lon_sec', maxvalue=59.999, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
+option: Decimal('loc_part_lon_sec', attribute=False, autofill=False, cli_name='loc_lon_sec', maxvalue=Decimal('59.999'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=3, required=False)
option: StrEnum('loc_part_lon_dir', attribute=False, autofill=False, cli_name='loc_lon_dir', multivalue=False, option_group=u'LOC Record', required=False, values=(u'E', u'W'))
-option: Float('loc_part_altitude', attribute=False, autofill=False, cli_name='loc_altitude', maxvalue=42849672.95, minvalue=-100000.0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_size', attribute=False, autofill=False, cli_name='loc_size', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_h_precision', attribute=False, autofill=False, cli_name='loc_h_precision', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
-option: Float('loc_part_v_precision', attribute=False, autofill=False, cli_name='loc_v_precision', maxvalue=90000000.0, minvalue=0.0, multivalue=False, option_group=u'LOC Record', required=False)
+option: Decimal('loc_part_altitude', attribute=False, autofill=False, cli_name='loc_altitude', maxvalue=Decimal('42849672.95'), minvalue=Decimal('-100000.00'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
+option: Decimal('loc_part_size', attribute=False, autofill=False, cli_name='loc_size', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
+option: Decimal('loc_part_h_precision', attribute=False, autofill=False, cli_name='loc_h_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
+option: Decimal('loc_part_v_precision', attribute=False, autofill=False, cli_name='loc_v_precision', maxvalue=Decimal('90000000.00'), minvalue=Decimal('0.0'), multivalue=False, option_group=u'LOC Record', precision=2, required=False)
option: MXRecord('mxrecord', attribute=True, autofill=False, cli_name='mx_rec', csv=True, multivalue=True, option_group=u'MX Record', required=False)
option: Int('mx_part_preference', attribute=False, autofill=False, cli_name='mx_preference', maxvalue=65535, minvalue=0, multivalue=False, option_group=u'MX Record', required=False)
option: Str('mx_part_exchanger', attribute=False, autofill=False, cli_name='mx_exchanger', multivalue=False, option_group=u'MX Record', required=False)
diff --git a/doc/guide/guide.org b/doc/guide/guide.org
index 68858166e..bca7b34fa 100644
--- a/doc/guide/guide.org
+++ b/doc/guide/guide.org
@@ -227,7 +227,7 @@ verbose = Flag('verbose', default=True)
specified when constructing =Int= parameter:
- /minvalue/ :: minimal value that this parameter accepts, defaults to =MININT=
- /maxvalue/ :: maximum value this parameter can accept, defaults to =MAXINT=
-- /Float/ :: floating point parameters that are stored in Python's float type. =Float= has
+- /Decimal/ :: floating point parameters that are stored in Python's Decimal type. =Decimal= has
the same two additional properties as =Int=. Unlike =Int=, there are no
default values for the minimal and maximum boundaries.
- /Bytes/ :: a parameter to represent binary data.
@@ -294,9 +294,9 @@ class tank(Object):
takes_params = (
StrEnum('species*', label=u'Species', doc=u'Fish species',
values=(u'Angelfish', u'Betta', u'Cichlid', u'Firemouth')),
- Float('height', label=u'Height', doc=u'height in mm', default=400.0),
- Float('width', label=u'Width', doc=u'width in mm', default=400.0),
- Float('depth', label=u'Depth', doc=u'Depth in mm', default=300.0)
+ Decimal('height', label=u'Height', doc=u'height in mm', default='400.0'),
+ Decimal('width', label=u'Width', doc=u'width in mm', default='400.0'),
+ Decimal('depth', label=u'Depth', doc=u'Depth in mm', default='300.0')
)
api.register(tank) (ref:register)
diff --git a/ipalib/__init__.py b/ipalib/__init__.py
index 29ba0bb90..1efeeab4a 100644
--- a/ipalib/__init__.py
+++ b/ipalib/__init__.py
@@ -878,7 +878,7 @@ from backend import Backend
from frontend import Command, LocalOrRemote, Updater
from frontend import Object, Method, Property
from crud import Create, Retrieve, Update, Delete, Search
-from parameters import DefaultFrom, Bool, Flag, Int, Float, Bytes, Str, IA5Str, Password
+from parameters import DefaultFrom, Bool, Flag, Int, Decimal, Bytes, Str, IA5Str, Password
from parameters import BytesEnum, StrEnum, AccessTime, File
from errors import SkipPluginModule
from text import _, ngettext, GettextFactory, NGettextFactory
diff --git a/ipalib/encoder.py b/ipalib/encoder.py
index f23e5659e..8d59bd316 100644
--- a/ipalib/encoder.py
+++ b/ipalib/encoder.py
@@ -20,6 +20,8 @@
Encoding capabilities.
"""
+from decimal import Decimal
+
class EncoderSettings(object):
"""
Container for encoder settings.
@@ -77,7 +79,7 @@ class Encoder(object):
return self.encoder_settings.encode_postprocessor(
var.encode(self.encoder_settings.encode_to)
)
- elif isinstance(var, (bool, float, int, long)):
+ elif isinstance(var, (bool, float, Decimal, int, long)):
return self.encoder_settings.encode_postprocessor(
str(var).encode(self.encoder_settings.encode_to)
)
@@ -131,7 +133,7 @@ class Encoder(object):
return self.encoder_settings.decode_postprocessor(
var.decode(self.encoder_settings.decode_from)
)
- elif isinstance(var, (bool, float, int, long)):
+ elif isinstance(var, (bool, float, Decimal, int, long)):
return var
elif isinstance(var, list):
return [self.decode(m) for m in var]
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,
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 8042c3a1b..abb2e90b8 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -25,7 +25,7 @@ import re
from ipalib.request import context
from ipalib import api, errors, output
from ipalib import Command
-from ipalib.parameters import Flag, Bool, Int, Float, Str, StrEnum, Any
+from ipalib.parameters import Flag, Bool, Int, Decimal, Str, StrEnum, Any
from ipalib.plugins.baseldap import *
from ipalib import _, ngettext
from ipalib.util import validate_zonemgr, normalize_zonemgr, validate_hostname
@@ -345,7 +345,12 @@ class DNSRecord(Str):
if not values:
return value
- new_values = [ part.normalize(values[part_id]) \
+ converted_values = [ part._convert_scalar(values[part_id]) \
+ if values[part_id] is not None else None
+ for part_id, part in enumerate(self.parts)
+ ]
+
+ new_values = [ part.normalize(converted_values[part_id]) \
for part_id, part in enumerate(self.parts) ]
value = self._convert_scalar(new_values)
@@ -626,10 +631,11 @@ class LOCRecord(DNSRecord):
minvalue=0,
maxvalue=59,
),
- Float('lat_sec?',
+ Decimal('lat_sec?',
label=_('Seconds Latitude'),
- minvalue=0.0,
- maxvalue=59.999,
+ minvalue='0.0',
+ maxvalue='59.999',
+ precision=3,
),
StrEnum('lat_dir',
label=_('Direction Latitude'),
@@ -645,34 +651,39 @@ class LOCRecord(DNSRecord):
minvalue=0,
maxvalue=59,
),
- Float('lon_sec?',
+ Decimal('lon_sec?',
label=_('Seconds Longtitude'),
- minvalue=0.0,
- maxvalue=59.999,
+ minvalue='0.0',
+ maxvalue='59.999',
+ precision=3,
),
StrEnum('lon_dir',
label=_('Direction Longtitude'),
values=(u'E', u'W',),
),
- Float('altitude',
+ Decimal('altitude',
label=_('Altitude'),
- minvalue=-100000.00,
- maxvalue=42849672.95,
+ minvalue='-100000.00',
+ maxvalue='42849672.95',
+ precision=2,
),
- Float('size?',
+ Decimal('size?',
label=_('Size'),
- minvalue=0.0,
- maxvalue=90000000.00,
+ minvalue='0.0',
+ maxvalue='90000000.00',
+ precision=2,
),
- Float('h_precision?',
+ Decimal('h_precision?',
label=_('Horizontal Precision'),
- minvalue=0.0,
- maxvalue=90000000.00,
+ minvalue='0.0',
+ maxvalue='90000000.00',
+ precision=2,
),
- Float('v_precision?',
+ Decimal('v_precision?',
label=_('Vertical Precision'),
- minvalue=0.0,
- maxvalue=90000000.00,
+ minvalue='0.0',
+ maxvalue='90000000.00',
+ precision=2,
),
)
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 8ec3a2f27..5a59ae654 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -31,6 +31,7 @@ Also see the `ipaserver.rpcserver` module.
"""
from types import NoneType
+from decimal import Decimal
import threading
import sys
import os
@@ -86,6 +87,9 @@ def xml_wrap(value):
)
if type(value) is str:
return Binary(value)
+ if type(value) is Decimal:
+ # transfer Decimal as a string
+ return unicode(value)
assert type(value) in (unicode, int, float, bool, NoneType)
return value
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 26850db55..955c11b7f 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -37,6 +37,7 @@ from ipapython.version import VERSION
import base64
import os
import string
+from decimal import Decimal
_not_found_template = """<html>
<head>
<title>404 Not Found</title>
@@ -385,6 +386,8 @@ def json_encode_binary(val):
return new_list
elif isinstance(val, str):
return {'__base64__' : base64.b64encode(val)}
+ elif isinstance(val, Decimal):
+ return {'__base64__' : base64.b64encode(str(val))}
else:
return val
diff --git a/make-lint b/make-lint
index ef2414970..5826c3229 100755
--- a/make-lint
+++ b/make-lint
@@ -61,7 +61,7 @@ class IPATypeChecker(TypeChecker):
'sortorder', 'csv', 'csv_separator', 'csv_skipspace'],
'ipalib.parameters.Bool': ['truths', 'falsehoods'],
'ipalib.parameters.Int': ['minvalue', 'maxvalue'],
- 'ipalib.parameters.Float': ['minvalue', 'maxvalue'],
+ 'ipalib.parameters.Decimal': ['minvalue', 'maxvalue', 'precision'],
'ipalib.parameters.Data': ['minlength', 'maxlength', 'length',
'pattern', 'pattern_errmsg'],
'ipalib.parameters.Enum': ['values'],
diff --git a/tests/test_ipalib/test_parameters.py b/tests/test_ipalib/test_parameters.py
index 5cb7abf2a..ad8d84044 100644
--- a/tests/test_ipalib/test_parameters.py
+++ b/tests/test_ipalib/test_parameters.py
@@ -25,6 +25,7 @@ Test the `ipalib.parameters` module.
import re
import sys
from types import NoneType
+from decimal import Decimal
from inspect import isclass
from tests.util import raises, ClassChecker, read_only
from tests.util import dummy_ugettext, assert_equal
@@ -1300,77 +1301,77 @@ class test_Int(ClassChecker):
assert o._convert_scalar(u'0x10') == 16
assert o._convert_scalar(u'020') == 16
-class test_Float(ClassChecker):
+class test_Decimal(ClassChecker):
"""
- Test the `ipalib.parameters.Float` class.
+ Test the `ipalib.parameters.Decimal` class.
"""
- _cls = parameters.Float
+ _cls = parameters.Decimal
def test_init(self):
"""
- Test the `ipalib.parameters.Float.__init__` method.
+ Test the `ipalib.parameters.Decimal.__init__` method.
"""
# Test with no kwargs:
o = self.cls('my_number')
- assert o.type is float
- assert isinstance(o, parameters.Float)
+ assert o.type is Decimal
+ assert isinstance(o, parameters.Decimal)
assert o.minvalue is None
assert o.maxvalue is None
# Test when min > max:
- e = raises(ValueError, self.cls, 'my_number', minvalue=22.5, maxvalue=15.1)
+ e = raises(ValueError, self.cls, 'my_number', minvalue=Decimal('22.5'), maxvalue=Decimal('15.1'))
assert str(e) == \
- "Float('my_number'): minvalue > maxvalue (minvalue=22.5, maxvalue=15.1)"
+ "Decimal('my_number'): minvalue > maxvalue (minvalue=22.5, maxvalue=15.1)"
def test_rule_minvalue(self):
"""
- Test the `ipalib.parameters.Float._rule_minvalue` method.
+ Test the `ipalib.parameters.Decimal._rule_minvalue` method.
"""
- o = self.cls('my_number', minvalue=3.1)
- assert o.minvalue == 3.1
+ o = self.cls('my_number', minvalue='3.1')
+ assert o.minvalue == Decimal('3.1')
rule = o._rule_minvalue
- translation = u'minvalue=%(minvalue)r'
+ translation = u'minvalue=%(minvalue)s'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
- for value in (3.2, 99.0):
+ for value in (Decimal('3.2'), Decimal('99.0')):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
- for value in (-1.2, 0.0, 3.0):
+ for value in (Decimal('-1.2'), Decimal('0.0'), Decimal('3.0')):
assert_equal(
rule(dummy, value),
- translation % dict(minvalue=3.1)
+ translation % dict(minvalue=Decimal('3.1'))
)
- assert dummy.message == 'must be at least %(minvalue)f'
+ assert dummy.message == 'must be at least %(minvalue)s'
assert dummy.called() is True
dummy.reset()
def test_rule_maxvalue(self):
"""
- Test the `ipalib.parameters.Float._rule_maxvalue` method.
+ Test the `ipalib.parameters.Decimal._rule_maxvalue` method.
"""
- o = self.cls('my_number', maxvalue=4.7)
- assert o.maxvalue == 4.7
+ o = self.cls('my_number', maxvalue='4.7')
+ assert o.maxvalue == Decimal('4.7')
rule = o._rule_maxvalue
translation = u'maxvalue=%(maxvalue)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
- for value in (-1.0, 0.1, 4.2):
+ for value in (Decimal('-1.0'), Decimal('0.1'), Decimal('4.2')):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
- for value in (5.3, 99.9):
+ for value in (Decimal('5.3'), Decimal('99.9')):
assert_equal(
rule(dummy, value),
- translation % dict(maxvalue=4.7)
+ translation % dict(maxvalue=Decimal('4.7'))
)
- assert dummy.message == 'can be at most %(maxvalue)f'
+ assert dummy.message == 'can be at most %(maxvalue)s'
assert dummy.called() is True
dummy.reset()
diff --git a/tests/test_xmlrpc/test_dns_plugin.py b/tests/test_xmlrpc/test_dns_plugin.py
index 00d4f9b7d..bded2ad42 100644
--- a/tests/test_xmlrpc/test_dns_plugin.py
+++ b/tests/test_xmlrpc/test_dns_plugin.py
@@ -598,7 +598,7 @@ class test_dns(Declarative):
'idnsname': [dnszone1],
'mxrecord': [u"0 %s" % dnszone1_mname],
'nsrecord': [dnszone1_mname],
- 'locrecord': [u"49 11 42.4 N 16 36 29.6 E 227.64"],
+ 'locrecord': [u"49 11 42.400 N 16 36 29.600 E 227.64"],
},
},
),