result from merge with gerrit
secop subdir only Change-Id: I65ab7049719b374ae3ec0259483e7e7d16aafcd1
This commit is contained in:
@ -30,29 +30,25 @@ from base64 import b64decode, b64encode
|
||||
|
||||
from secop.errors import BadValueError, \
|
||||
ConfigError, ProgrammingError, ProtocolError
|
||||
from secop.lib import clamp
|
||||
from secop.lib import clamp, generalConfig
|
||||
from secop.lib.enum import Enum
|
||||
from secop.parse import Parser
|
||||
from secop.properties import HasProperties, Property
|
||||
|
||||
# Only export these classes for 'from secop.datatypes import *'
|
||||
__all__ = [
|
||||
'DataType', 'get_datatype',
|
||||
'FloatRange', 'IntRange', 'ScaledInteger',
|
||||
'BoolType', 'EnumType',
|
||||
'BLOBType', 'StringType', 'TextType',
|
||||
'TupleOf', 'ArrayOf', 'StructOf',
|
||||
'CommandType', 'StatusType',
|
||||
]
|
||||
|
||||
# *DEFAULT* limits for IntRange/ScaledIntegers transport serialisation
|
||||
DEFAULT_MIN_INT = -16777216
|
||||
DEFAULT_MAX_INT = 16777216
|
||||
UNLIMITED = 1 << 64 # internal limit for integers, is probably high enough for any datatype size
|
||||
generalConfig.defaults['lazy_number_validation'] = False
|
||||
|
||||
Parser = Parser()
|
||||
|
||||
|
||||
class DiscouragedConversion(BadValueError):
|
||||
"""the discouraged conversion string - > float happened"""
|
||||
log_message = True
|
||||
|
||||
|
||||
# base class for all DataTypes
|
||||
class DataType(HasProperties):
|
||||
"""base class for all data types"""
|
||||
@ -63,7 +59,7 @@ class DataType(HasProperties):
|
||||
def __call__(self, value):
|
||||
"""check if given value (a python obj) is valid for this datatype
|
||||
|
||||
returns the value or raises an appropriate exception"""
|
||||
returns the (possibly converted) value or raises an appropriate exception"""
|
||||
raise NotImplementedError
|
||||
|
||||
def from_string(self, text):
|
||||
@ -192,9 +188,15 @@ class FloatRange(DataType):
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
value = float(value)
|
||||
value += 0.0 # do not accept strings here
|
||||
except Exception:
|
||||
raise BadValueError('Can not convert %r to float' % value) from None
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
raise BadValueError('Can not convert %r to float' % value) from None
|
||||
if not generalConfig.lazy_number_validation:
|
||||
raise DiscouragedConversion('automatic string to float conversion no longer supported') from None
|
||||
|
||||
# map +/-infty to +/-max possible number
|
||||
value = clamp(-sys.float_info.max, value, sys.float_info.max)
|
||||
|
||||
@ -232,6 +234,18 @@ class FloatRange(DataType):
|
||||
return ' '.join([self.fmtstr % value, unit])
|
||||
return self.fmtstr % value
|
||||
|
||||
def problematic_range(self, target_type):
|
||||
"""check problematic range
|
||||
|
||||
returns True when self.min or self.max is given, not 0 and equal to the same limit on target_type.
|
||||
"""
|
||||
value_info = self.get_info()
|
||||
target_info = target_type.get_info()
|
||||
minval = value_info.get('min') # None when -infinite
|
||||
maxval = value_info.get('max') # None when +infinite
|
||||
return ((minval and minval == target_info.get('min')) or
|
||||
(maxval and maxval == target_info.get('max')))
|
||||
|
||||
def compatible(self, other):
|
||||
if not isinstance(other, (FloatRange, ScaledInteger)):
|
||||
raise BadValueError('incompatible datatypes')
|
||||
@ -265,10 +279,16 @@ class IntRange(DataType):
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
fvalue = float(value)
|
||||
fvalue = value + 0.0 # do not accept strings here
|
||||
value = int(value)
|
||||
except Exception:
|
||||
raise BadValueError('Can not convert %r to int' % value) from None
|
||||
try:
|
||||
fvalue = float(value)
|
||||
value = int(value)
|
||||
except Exception:
|
||||
raise BadValueError('Can not convert %r to int' % value) from None
|
||||
if not generalConfig.lazy_number_validation:
|
||||
raise DiscouragedConversion('automatic string to float conversion no longer supported') from None
|
||||
if not self.min <= value <= self.max or round(fvalue) != fvalue:
|
||||
raise BadValueError('%r should be an int between %d and %d' %
|
||||
(value, self.min, self.max))
|
||||
@ -298,13 +318,15 @@ class IntRange(DataType):
|
||||
return '%d' % value
|
||||
|
||||
def compatible(self, other):
|
||||
if isinstance(other, IntRange):
|
||||
if isinstance(other, (IntRange, FloatRange, ScaledInteger)):
|
||||
other(self.min)
|
||||
other(self.max)
|
||||
return
|
||||
# this will accept some EnumType, BoolType
|
||||
for i in range(self.min, self.max + 1):
|
||||
other(i)
|
||||
if isinstance(other, (EnumType, BoolType)):
|
||||
# the following loop will not cycle more than the number of Enum elements
|
||||
for i in range(self.min, self.max + 1):
|
||||
other(i)
|
||||
raise BadValueError('incompatible datatypes')
|
||||
|
||||
|
||||
class ScaledInteger(DataType):
|
||||
@ -369,9 +391,14 @@ class ScaledInteger(DataType):
|
||||
|
||||
def __call__(self, value):
|
||||
try:
|
||||
value = float(value)
|
||||
value += 0.0 # do not accept strings here
|
||||
except Exception:
|
||||
raise BadValueError('Can not convert %r to float' % value) from None
|
||||
try:
|
||||
value = float(value)
|
||||
except Exception:
|
||||
raise BadValueError('Can not convert %r to float' % value) from None
|
||||
if not generalConfig.lazy_number_validation:
|
||||
raise DiscouragedConversion('automatic string to float conversion no longer supported') from None
|
||||
prec = max(self.scale, abs(value * self.relative_resolution),
|
||||
self.absolute_resolution)
|
||||
if self.min - prec <= value <= self.max + prec:
|
||||
@ -849,7 +876,7 @@ class ImmutableDict(dict):
|
||||
class StructOf(DataType):
|
||||
"""data structure with named fields
|
||||
|
||||
:param optional: a list of optional members (default None: all members optional)
|
||||
:param optional: a list of optional members
|
||||
:param members: names as keys and types as values for all members
|
||||
"""
|
||||
def __init__(self, optional=None, **members):
|
||||
@ -857,7 +884,7 @@ class StructOf(DataType):
|
||||
self.members = members
|
||||
if not members:
|
||||
raise BadValueError('Empty structs are not allowed!')
|
||||
self.optional = list(members) if optional is None else list(optional)
|
||||
self.optional = list(optional or [])
|
||||
for name, subtype in list(members.items()):
|
||||
if not isinstance(subtype, DataType):
|
||||
raise ProgrammingError(
|
||||
@ -1118,37 +1145,26 @@ def floatargs(kwds):
|
||||
DATATYPES = dict(
|
||||
bool = lambda **kwds:
|
||||
BoolType(),
|
||||
|
||||
int = lambda min, max, **kwds:
|
||||
IntRange(minval=min, maxval=max),
|
||||
|
||||
scaled = lambda scale, min, max, **kwds:
|
||||
ScaledInteger(scale=scale, minval=min*scale, maxval=max*scale, **floatargs(kwds)),
|
||||
|
||||
double = lambda min=None, max=None, **kwds:
|
||||
FloatRange(minval=min, maxval=max, **floatargs(kwds)),
|
||||
|
||||
blob = lambda maxbytes, minbytes=0, **kwds:
|
||||
BLOBType(minbytes=minbytes, maxbytes=maxbytes),
|
||||
|
||||
string = lambda minchars=0, maxchars=None, isUTF8=False, **kwds:
|
||||
StringType(minchars=minchars, maxchars=maxchars, isUTF8=isUTF8),
|
||||
|
||||
array = lambda maxlen, members, minlen=0, pname='', **kwds:
|
||||
ArrayOf(get_datatype(members, pname), minlen=minlen, maxlen=maxlen),
|
||||
|
||||
tuple = lambda members, pname='', **kwds:
|
||||
TupleOf(*tuple((get_datatype(t, pname) for t in members))),
|
||||
|
||||
enum = lambda members, pname='', **kwds:
|
||||
EnumType(pname, members=members),
|
||||
|
||||
struct = lambda members, optional=None, pname='', **kwds:
|
||||
StructOf(optional, **dict((n, get_datatype(t, pname)) for n, t in list(members.items()))),
|
||||
|
||||
command = lambda argument=None, result=None, pname='', **kwds:
|
||||
CommandType(get_datatype(argument, pname), get_datatype(result)),
|
||||
|
||||
limit = lambda members, pname='', **kwds:
|
||||
LimitsType(get_datatype(members, pname)),
|
||||
)
|
||||
|
Reference in New Issue
Block a user