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) + 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 - if (self.minvalue > self.maxvalue) and (self.minvalue is not None and self.maxvalue is not None): + 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 = """