mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-28 01:41:14 -06:00
092dd8db12
Having float type as a base type for floating point parameters in ipalib introduces several issues, e.g. problem with representation or value comparison. Python language provides a Decimal type which help overcome these issues. This patch replaces a float type and Float parameter with a decimal.Decimal type in Decimal parameter. A precision attribute was added to Decimal parameter that can be used to limit a number of decimal places in parameter representation. This approach fixes a problem with API.txt validation where comparison of float values may fail on different architectures due to float representation error. In order to safely transfer the parameter value over RPC it is being converted to string which is then converted back to decimal.Decimal number on a server side. https://fedorahosted.org/freeipa/ticket/2260
206 lines
7.7 KiB
Python
206 lines
7.7 KiB
Python
# Authors:
|
|
# Pavel Zuna <pzuna@redhat.com>
|
|
#
|
|
# Copyright (C) 2009 Red Hat
|
|
# see file 'COPYING' for use and warranty information
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
Encoding capabilities.
|
|
"""
|
|
|
|
from decimal import Decimal
|
|
|
|
class EncoderSettings(object):
|
|
"""
|
|
Container for encoder settings.
|
|
"""
|
|
encode_to = 'utf-8'
|
|
encode_none = False
|
|
encode_dict_keys = False
|
|
encode_dict_keys_postprocess = True
|
|
encode_dict_vals = True
|
|
encode_dict_vals_postprocess = True
|
|
encode_postprocessor = staticmethod(lambda x: x)
|
|
|
|
decode_from = 'utf-8'
|
|
decode_none = False
|
|
decode_dict_keys = False
|
|
decode_dict_keys_postprocess = True
|
|
decode_dict_vals = True
|
|
decode_dict_vals_postprocess = True
|
|
decode_dict_vals_table = dict()
|
|
decode_dict_vals_table_keygen = staticmethod(lambda x, y: x)
|
|
decode_postprocessor = staticmethod(lambda x: x)
|
|
|
|
|
|
class Encoder(object):
|
|
"""
|
|
Base class implementing encoding of python scalar types to strings
|
|
and vise-versa.
|
|
"""
|
|
|
|
encoder_settings = EncoderSettings()
|
|
|
|
def __init__(self):
|
|
# each instance should have its own settings
|
|
self.encoder_settings = EncoderSettings()
|
|
|
|
def _decode_dict_val(self, key, val):
|
|
f = self.encoder_settings.decode_dict_vals_table.get(
|
|
self.encoder_settings.decode_dict_vals_table_keygen(key, val)
|
|
)
|
|
if f:
|
|
return val
|
|
return self.decode(val)
|
|
|
|
def encode(self, var):
|
|
"""
|
|
Encode any python built-in python type variable into `self.encode_to`.
|
|
|
|
Compound types have their individual members encoded.
|
|
|
|
Returns an encoded copy of 'var'.
|
|
"""
|
|
if isinstance(var, str):
|
|
return var
|
|
elif isinstance(var, unicode):
|
|
return self.encoder_settings.encode_postprocessor(
|
|
var.encode(self.encoder_settings.encode_to)
|
|
)
|
|
elif isinstance(var, (bool, float, Decimal, int, long)):
|
|
return self.encoder_settings.encode_postprocessor(
|
|
str(var).encode(self.encoder_settings.encode_to)
|
|
)
|
|
elif isinstance(var, list):
|
|
return [self.encode(m) for m in var]
|
|
elif isinstance(var, tuple):
|
|
return tuple(self.encode(m) for m in var)
|
|
elif isinstance(var, dict):
|
|
if self.encoder_settings.encode_dict_keys:
|
|
dct = dict()
|
|
if not self.encoder_settings.encode_dict_keys_postprocess:
|
|
tmp = self.encoder_settings.encode_postprocessor
|
|
self.encoder_settings.encode_postprocessor = lambda x: x
|
|
for (k, v) in var.iteritems():
|
|
dct[self.encode(k)] = v
|
|
if not self.encoder_settings.encode_dict_keys_postprocess:
|
|
self.encoder_settings.encode_postprocessor = tmp
|
|
else:
|
|
dct = dict(var)
|
|
if self.encoder_settings.encode_dict_vals:
|
|
if not self.encoder_settings.encode_dict_vals_postprocess:
|
|
tmp = self.encoder_settings.encode_postprocessor
|
|
self.encoder_settings.encode_postprocessor = lambda x: x
|
|
for (k, v) in dct.iteritems():
|
|
dct[k] = self.encode(v)
|
|
if not self.encoder_settings.encode_dict_vals_postprocess:
|
|
self.encoder_settings.encode_postprocessor = tmp
|
|
return dct
|
|
elif var is None:
|
|
if self.encoder_settings.encode_none:
|
|
return self.encoder_settings.encode_postprocessor(
|
|
str(var).encode(self.encoder_settings.encode_to)
|
|
)
|
|
return None
|
|
raise TypeError('python built-in type expected, got \'%s\'', type(var))
|
|
|
|
def decode(self, var):
|
|
"""
|
|
Decode strings in `self.decode_from` into python strings.
|
|
|
|
Compound types have their individual members decoded.
|
|
|
|
Dictionaries can have their values decoded into other types
|
|
by looking up keys in `self.decode_dict_vals_table`.
|
|
|
|
Returns a decoded copy of 'var'.
|
|
"""
|
|
if isinstance(var, unicode):
|
|
return var
|
|
elif isinstance(var, str):
|
|
return self.encoder_settings.decode_postprocessor(
|
|
var.decode(self.encoder_settings.decode_from)
|
|
)
|
|
elif isinstance(var, (bool, float, Decimal, int, long)):
|
|
return var
|
|
elif isinstance(var, list):
|
|
return [self.decode(m) for m in var]
|
|
elif isinstance(var, tuple):
|
|
return tuple(self.decode(m) for m in var)
|
|
elif isinstance(var, dict):
|
|
if self.encoder_settings.decode_dict_keys:
|
|
dct = dict()
|
|
if not self.encoder_settings.decode_dict_keys_postprocess:
|
|
tmp = self.encoder_settings.decode_postprocessor
|
|
self.encoder_settings.decode_postprocessor = lambda x: x
|
|
for (k, v) in var.iteritems():
|
|
dct[self.decode(k)] = v
|
|
if not self.encoder_settings.decode_dict_keys_postprocess:
|
|
self.encoder_settings.decode_postprocessor = tmp
|
|
else:
|
|
dct = dict(var)
|
|
if self.encoder_settings.decode_dict_vals:
|
|
if not self.encoder_settings.decode_dict_vals_postprocess:
|
|
tmp = self.encoder_settings.decode_postprocessor
|
|
self.encoder_settings.decode_postprocessor = lambda x: x
|
|
for (k, v) in dct.iteritems():
|
|
dct[k] = self._decode_dict_val(k, v)
|
|
if not self.encoder_settings.decode_dict_vals_postprocess:
|
|
self.encoder_settings.decode_postprocessor = tmp
|
|
return dct
|
|
elif var is None:
|
|
if self.encoder_settings.decode_none:
|
|
return self.encoder_settings.decode_postprocessor(
|
|
str(var).decode(self.encoder_settings.decode_from)
|
|
)
|
|
return None
|
|
raise TypeError('python built-in type expected, got \'%s\'', type(var))
|
|
|
|
## ENCODER METHOD DECORATORS
|
|
|
|
def encode_args(*outer_args):
|
|
def decorate(f):
|
|
def new_f(*args, **kwargs):
|
|
assert isinstance(args[0], Encoder), \
|
|
'first argument not Encoder instance'
|
|
new_args = list(args)
|
|
for a in outer_args:
|
|
if isinstance(a, int):
|
|
if a < len(args):
|
|
new_args[a] = args[0].encode(args[a])
|
|
elif isinstance(a, basestring):
|
|
if a in kwargs:
|
|
kwargs[a] = args[0].encode(kwargs[a])
|
|
else:
|
|
raise TypeError(
|
|
'encode_args takes a list of ints and basestrings'
|
|
)
|
|
return f(*new_args, **kwargs)
|
|
new_f.func_name = f.func_name
|
|
return new_f
|
|
return decorate
|
|
|
|
|
|
def decode_retval():
|
|
def decorate(f):
|
|
def new_f(*args, **kwargs):
|
|
assert isinstance(args[0], Encoder), \
|
|
'first argument not Encoder instance'
|
|
return args[0].decode(f(*args, **kwargs))
|
|
new_f.func_name = f.func_name
|
|
return new_f
|
|
return decorate
|
|
|