__author__ = 'dstrohl'
__all__ = ['DataTypeList', 'DataTypeStr', 'DataTypeFloat', 'DataTypeInt', 'DataTypeDict', 'DataTypeBoolean',
'DataTypeLooseVersion', 'DataTypeStrictVersion',
'DataTypeGenerator', 'data_type_generator']
import ast
import copy
from AdvConfigMgr.utils import make_list, convert_to_boolean, slugify, get_after, get_before
from AdvConfigMgr.config_validation import ValidationError
from unicodedata import normalize
from AdvConfigMgr.utils.unset import _UNSET
from distutils.version import LooseVersion, StrictVersion
'''
class ItemKey(object):
_sec_opt_sep = '.'
_def_glob_chars = '*?[]!'
_def_no_glob_chars = '_'
def __init__(self, name=None, **kwargs):
self._section = None
self._sect_compare = None
self._option = None
self._dot = False
self._xform_class = Xform()
glob = kwargs.pop('glob', None)
if glob:
self._glob_chars = self._def_glob_chars
else:
self._glob_chars = self._def_no_glob_chars
self._save_and_xform(name=name, **kwargs)
def _set_sec(self, section):
self._section = self._xform_class.section(section, self._glob_chars)
def _set_opt(self, option):
self._option = self._xform_class.option(option, self._glob_chars)
self._sect_compare = self._xform_class.section(option, self._glob_chars)
def _save_dot_not(self, name):
if self._sec_opt_sep is not None and self._sec_opt_sep in name:
self._set_sec(get_before(name, self._sec_opt_sep))
self._set_opt(get_after(name, self._sec_opt_sep))
self._dot = True
return None
if self._sec_opt_sep is None and self._sec_opt_sep in name:
msg = 'Dot Notation disabled : {}'.format(name)
raise AttributeError(msg)
return name
def _save_and_xform(self, name=None, **kwargs):
name = kwargs.get('name', name)
if name is not None:
if type(name) == type(self):
self._section = name.section
self._option = name.option
self._sect_compare = name._sect_compare
elif isinstance(name, str):
if self._save_dot_not(name) is not None:
self._set_sec(name)
self._set_opt(name)
if 'section' in kwargs:
section = kwargs['section']
if isinstance(section, str):
if self._save_dot_not(section) is not None:
self._set_sec(section)
elif type(section) == type(self):
self._section = name.section
else:
self._section = section
if 'option' in kwargs:
option = kwargs['option']
if isinstance(option, str):
if self._save_dot_not(option) is not None:
self._set_opt(option)
elif type(option) == type(self):
self._option = name.option
else:
self._option = option
return self
@property
def section(self):
return self._section
@property
def option(self):
return self._option
@property
def s(self):
return self._section
@property
def o(self):
return self._option
@property
def has_both(self):
return self._section == self._sect_compare
def __str__(self):
if self._section == self._sect_compare:
return self.s
else:
return '{}{}{}'.format(self.s, self._sec_opt_sep, self.o)
def __call__(self, name=None, **kwargs):
return self._save_and_xform(name, **kwargs)
def __eq__(self, other):
if isinstance(other, str):
return str(self) == other
elif type(other) == type(self):
return str(self) == str(other)
def __repr__(self):
return str(self)
'''
class DataTypeGenerator(object):
def __init__(self, *args):
self._type_classes = {}
for t in args:
self.register_type(t)
def register_type(self, dt):
if not issubclass(dt, DataTypeBase):
raise TypeError('Data Type class is not a sub-class of DataTypeBase')
self._type_classes[dt.name] = dt
def get(self, dt):
if dt in self._type_classes:
return self._type_classes[dt]
else:
msg = 'Datatype {} not recognized for: {}'.format(type(dt).__name__, dt)
raise TypeError(msg)
def __call__(self, dt):
return self.get(dt)
[docs]class DataTypeBase(object):
name = 'str'
_type_class = str
def __init__(self, validations=None, allow_empty=True, empty_type=_UNSET):
"""
:param allow_empty: set to False if validation should be raised on empty or blank fields.
:param empty_types: set to a tuple of types that are considered empty
:param validations: a list or tuple of validation classes to run.
"""
if validations is not None:
self.validations = make_list(validations)
else:
self.validations = None
self.empty_type = empty_type
self.allow_empty = allow_empty
def __call__(self, value):
return self.validated(value)
def add_validations(self, validations):
self.validations = make_list(validations)
def auto_convert(self, value):
if isinstance(value, self._type_class):
return value
else:
return self._type_class(value)
[docs] def validated(self, value):
"""
Runs all validations and returns the value if validated.
:param value: value to be validated
:return:
"""
if self.allow_empty:
if value == self.empty_type:
return value
if self._validate_datatype(value):
return self._validations(value)
else:
tmp_msg = '%r is does not match the datatype requirement of %s' % (value, self.name)
raise ValidationError(tmp_msg)
# return self._validations(value) and self._validate_datatype(value)
def _validations(self, value):
if self.validations is not None:
for v in self.validations:
v.validate(value)
return value
def _validate_datatype(self, value):
"""
Returns a True/False depending on if the object matches the datatype defined.
Can be overwritten to validate the datatype.
"""
return isinstance(value, self._type_class)
@staticmethod
def _convert_to_string(value):
"""
Should return a string version of the value passed
Shoudl be over-ridden for types that "str" does not work for.
"""
tmp_str = str(value)
return tmp_str
def _convert_from_string(self, value):
"""
Should returns an object matching the datatype from a string
Should be over-ridden for non-string types
"""
tmp_ret = self._type_class(ast.literal_eval(value))
return tmp_ret
[docs] def to_string(self, value):
"""
Returns a string version of the value passed.
"""
return self._convert_to_string(value)
[docs] def from_string(self, value, validate=True):
"""
Returns an object matching the datatype from a string
"""
return self._convert_from_string(value)
def __repr__(self):
return 'Datatype Validator for: %s' % self.name
class DataTypeStr(DataTypeBase):
name = 'str'
@staticmethod
def _convert_from_string(value):
return value
@staticmethod
def _convert_to_string(value):
return value
class DataTypeInt(DataTypeBase):
name = 'int'
_type_class = int
class DataTypeFloat(DataTypeBase):
name = 'float'
_type_class = float
class DataTypeList(DataTypeBase):
name = 'list'
_type_class = list
class DataTypeDict(DataTypeBase):
name = 'dict'
_type_class = dict
class DataTypeBoolean(DataTypeBase):
name = 'bool'
_type_class = bool
def auto_convert(self, value):
if isinstance(value, self._type_class):
return value
else:
return convert_to_boolean(value)
@staticmethod
def _convert_to_string(value):
if value:
return "YES"
else:
return "NO"
def _convert_from_string(self, value):
return convert_to_boolean(value)
class DataTypeLooseVersion(DataTypeBase):
name = 'LooseVersion'
_type_class = LooseVersion
class DataTypeStrictVersion(DataTypeBase):
name = 'StrictVersion'
_type_class = StrictVersion
data_type_generator = DataTypeGenerator(DataTypeFloat, DataTypeList, DataTypeStr,
DataTypeInt, DataTypeDict, DataTypeBoolean)