Initial work on new configdata
This commit is contained in:
parent
c2e75bf2fd
commit
7e52eb7b0e
@ -134,11 +134,14 @@ class BaseType:
|
|||||||
"""Get the type's valid values for documentation."""
|
"""Get the type's valid values for documentation."""
|
||||||
return self.valid_values
|
return self.valid_values
|
||||||
|
|
||||||
def _basic_validation(self, value):
|
def _basic_validation(self, value, pytype=None):
|
||||||
"""Do some basic validation for the value (empty, non-printable chars).
|
"""Do some basic validation for the value (empty, non-printable chars).
|
||||||
|
|
||||||
|
Also does a Python typecheck on the value (if coming from YAML/Python).
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
value: The value to check.
|
value: The value to check.
|
||||||
|
pytype: If given, a Python type to check the value against.
|
||||||
"""
|
"""
|
||||||
if not value:
|
if not value:
|
||||||
if self.none_ok:
|
if self.none_ok:
|
||||||
@ -146,29 +149,17 @@ class BaseType:
|
|||||||
else:
|
else:
|
||||||
raise configexc.ValidationError(value, "may not be empty!")
|
raise configexc.ValidationError(value, "may not be empty!")
|
||||||
|
|
||||||
|
if isinstance(value, str):
|
||||||
if any(ord(c) < 32 or ord(c) == 0x7f for c in value):
|
if any(ord(c) < 32 or ord(c) == 0x7f for c in value):
|
||||||
raise configexc.ValidationError(value, "may not contain "
|
raise configexc.ValidationError(value, "may not contain "
|
||||||
"unprintable chars!")
|
"unprintable chars!")
|
||||||
|
|
||||||
def transform(self, value):
|
if pytype is not None and not isinstance(value, pytype):
|
||||||
"""Transform the setting value.
|
raise configexc.ValidationError(
|
||||||
|
value, "expected a value of type {} but got {}".format(
|
||||||
|
pytype, type(value)))
|
||||||
|
|
||||||
This method can assume the value is indeed a valid value.
|
def _validate_valid_values(self, value):
|
||||||
|
|
||||||
The default implementation returns the original value.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value: The original string value.
|
|
||||||
|
|
||||||
Return:
|
|
||||||
The transformed value.
|
|
||||||
"""
|
|
||||||
if not value:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return value
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
"""Validate value against possible values.
|
"""Validate value against possible values.
|
||||||
|
|
||||||
The default implementation checks the value against self.valid_values
|
The default implementation checks the value against self.valid_values
|
||||||
@ -177,7 +168,7 @@ class BaseType:
|
|||||||
Args:
|
Args:
|
||||||
value: The value to validate.
|
value: The value to validate.
|
||||||
"""
|
"""
|
||||||
self._basic_validation(value)
|
# FIXME:conf still needed?
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return
|
||||||
if self.valid_values is not None:
|
if self.valid_values is not None:
|
||||||
@ -189,6 +180,45 @@ class BaseType:
|
|||||||
raise NotImplementedError("{} does not implement validate.".format(
|
raise NotImplementedError("{} does not implement validate.".format(
|
||||||
self.__class__.__name__))
|
self.__class__.__name__))
|
||||||
|
|
||||||
|
def from_str(self, value):
|
||||||
|
"""Get the setting value from a string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The original string value.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
The transformed value.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def from_py(self, value):
|
||||||
|
"""Get the setting value from a Python value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value: The value we got from Python/YAML.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
The transformed value.
|
||||||
|
|
||||||
|
Raise:
|
||||||
|
configexc.ValidationError if the value was invalid.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def to_str(self, value):
|
||||||
|
"""Get a string from the setting value.
|
||||||
|
|
||||||
|
The resulting string should be parseable again by from_str.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
# def to_py(self, value):
|
||||||
|
# """Get a Python/YAML value from the setting value.
|
||||||
|
|
||||||
|
# The resulting value should be parseable again by from_py.
|
||||||
|
# """
|
||||||
|
# raise NotImplementedError
|
||||||
|
|
||||||
def complete(self):
|
def complete(self):
|
||||||
"""Return a list of possible values for completion.
|
"""Return a list of possible values for completion.
|
||||||
|
|
||||||
@ -223,19 +253,25 @@ class MappingType(BaseType):
|
|||||||
|
|
||||||
MAPPING = {}
|
MAPPING = {}
|
||||||
|
|
||||||
def __init__(self, none_ok=False,
|
def __init__(self, none_ok=False, valid_values=None):
|
||||||
valid_values=None):
|
|
||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
self.valid_values = valid_values
|
self.valid_values = valid_values
|
||||||
|
|
||||||
def validate(self, value):
|
def from_py(self, value):
|
||||||
super().validate(value.lower())
|
self._basic_validation(value, pytype=str)
|
||||||
|
|
||||||
def transform(self, value):
|
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
|
self._validate_valid_values(value.lower())
|
||||||
return self.MAPPING[value.lower()]
|
return self.MAPPING[value.lower()]
|
||||||
|
|
||||||
|
def from_str(self, value):
|
||||||
|
return self.from_py(value)
|
||||||
|
|
||||||
|
def to_str(self, value):
|
||||||
|
reverse_mapping = {v: k for k, v in self.MAPPING.items()}
|
||||||
|
assert len(self.MAPPING) == len(reverse_mapping)
|
||||||
|
return reverse_mapping[value]
|
||||||
|
|
||||||
|
|
||||||
class String(BaseType):
|
class String(BaseType):
|
||||||
|
|
||||||
@ -265,16 +301,11 @@ class String(BaseType):
|
|||||||
self.forbidden = forbidden
|
self.forbidden = forbidden
|
||||||
self._completions = completions
|
self._completions = completions
|
||||||
|
|
||||||
def validate(self, value):
|
def from_py(self, value):
|
||||||
self._basic_validation(value)
|
self._basic_validation(value, pytype=str)
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return None
|
||||||
|
self._validate_valid_values(value)
|
||||||
if self.valid_values is not None:
|
|
||||||
if value not in self.valid_values:
|
|
||||||
raise configexc.ValidationError(
|
|
||||||
value,
|
|
||||||
"valid values: {}".format(', '.join(self.valid_values)))
|
|
||||||
|
|
||||||
if self.forbidden is not None and any(c in value
|
if self.forbidden is not None and any(c in value
|
||||||
for c in self.forbidden):
|
for c in self.forbidden):
|
||||||
@ -287,6 +318,11 @@ class String(BaseType):
|
|||||||
raise configexc.ValidationError(value, "must be at most {} chars "
|
raise configexc.ValidationError(value, "must be at most {} chars "
|
||||||
"long!".format(self.maxlen))
|
"long!".format(self.maxlen))
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def from_str(self, value):
|
||||||
|
return self.from_py(value)
|
||||||
|
|
||||||
def complete(self):
|
def complete(self):
|
||||||
if self._completions is not None:
|
if self._completions is not None:
|
||||||
return self._completions
|
return self._completions
|
||||||
@ -298,16 +334,18 @@ class UniqueCharString(String):
|
|||||||
|
|
||||||
"""A string which may not contain duplicate chars."""
|
"""A string which may not contain duplicate chars."""
|
||||||
|
|
||||||
def validate(self, value):
|
def from_py(self, value):
|
||||||
super().validate(value)
|
value = super().from_py(value)
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return None
|
||||||
|
|
||||||
# Check for duplicate values
|
# Check for duplicate values
|
||||||
if len(set(value)) != len(value):
|
if len(set(value)) != len(value):
|
||||||
raise configexc.ValidationError(
|
raise configexc.ValidationError(
|
||||||
value, "String contains duplicate values!")
|
value, "String contains duplicate values!")
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class List(BaseType):
|
class List(BaseType):
|
||||||
|
|
||||||
@ -329,23 +367,23 @@ class List(BaseType):
|
|||||||
def get_valid_values(self):
|
def get_valid_values(self):
|
||||||
return self.valtype.get_valid_values()
|
return self.valtype.get_valid_values()
|
||||||
|
|
||||||
def transform(self, value):
|
def from_str(self, value):
|
||||||
|
try:
|
||||||
|
json_val = json.loads(value)
|
||||||
|
except ValueError as e:
|
||||||
|
raise configexc.ValidationError(value, str(e))
|
||||||
|
return self.from_py(json_val)
|
||||||
|
|
||||||
|
def from_py(self, value):
|
||||||
|
self._basic_validation(value, pytype=list)
|
||||||
if not value:
|
if not value:
|
||||||
return None
|
return None
|
||||||
else:
|
|
||||||
return [self.valtype.transform(v.strip())
|
|
||||||
for v in value.split(',')]
|
|
||||||
|
|
||||||
def validate(self, value):
|
if self.length is not None and len(value) != self.length:
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
vals = value.split(',')
|
|
||||||
if self.length is not None and len(vals) != self.length:
|
|
||||||
raise configexc.ValidationError(value, "Exactly {} values need to "
|
raise configexc.ValidationError(value, "Exactly {} values need to "
|
||||||
"be set!".format(self.length))
|
"be set!".format(self.length))
|
||||||
for val in vals:
|
|
||||||
self.valtype.validate(val.strip())
|
return [self.valtype.from_py(v) for v in value]
|
||||||
|
|
||||||
|
|
||||||
class FlagList(List):
|
class FlagList(List):
|
||||||
@ -364,19 +402,13 @@ class FlagList(List):
|
|||||||
super().__init__(BaseType(), none_ok)
|
super().__init__(BaseType(), none_ok)
|
||||||
self.valtype.valid_values = valid_values
|
self.valtype.valid_values = valid_values
|
||||||
|
|
||||||
def validate(self, value):
|
def from_py(self, value):
|
||||||
if self.valtype.valid_values is not None:
|
vals = super().from_py(value)
|
||||||
super().validate(value)
|
|
||||||
else:
|
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
vals = super().transform(value)
|
|
||||||
|
|
||||||
# Check for duplicate values
|
# Check for duplicate values
|
||||||
if len(set(vals)) != len(vals):
|
if len(set(vals)) != len(vals):
|
||||||
raise configexc.ValidationError(
|
raise configexc.ValidationError(
|
||||||
value, "List contains duplicate values!")
|
value, "List contains duplicate values!")
|
||||||
|
return vals
|
||||||
|
|
||||||
def complete(self):
|
def complete(self):
|
||||||
valid_values = self.valtype.valid_values
|
valid_values = self.valtype.valid_values
|
||||||
@ -407,17 +439,18 @@ class Bool(BaseType):
|
|||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
self.valid_values = ValidValues('true', 'false')
|
self.valid_values = ValidValues('true', 'false')
|
||||||
|
|
||||||
def transform(self, value):
|
def from_py(self, value):
|
||||||
if not value:
|
self._basic_validation(value, pytype=bool)
|
||||||
return None
|
return value
|
||||||
else:
|
|
||||||
return BOOLEAN_STATES[value.lower()]
|
|
||||||
|
|
||||||
def validate(self, value):
|
def from_str(self, value):
|
||||||
self._basic_validation(value)
|
self._basic_validation(value)
|
||||||
if not value:
|
if not value:
|
||||||
return
|
return None
|
||||||
elif value.lower() not in BOOLEAN_STATES:
|
|
||||||
|
try:
|
||||||
|
return BOOLEAN_STATES[value.lower()]
|
||||||
|
except KeyError:
|
||||||
raise configexc.ValidationError(value, "must be a boolean!")
|
raise configexc.ValidationError(value, "must be a boolean!")
|
||||||
|
|
||||||
|
|
||||||
@ -429,17 +462,15 @@ class BoolAsk(Bool):
|
|||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
self.valid_values = ValidValues('true', 'false', 'ask')
|
self.valid_values = ValidValues('true', 'false', 'ask')
|
||||||
|
|
||||||
def transform(self, value):
|
def from_py(self, value):
|
||||||
if value.lower() == 'ask':
|
# basic validation unneeded if it's == 'ask' and done by Bool if we call
|
||||||
|
# super().from_py
|
||||||
|
if isinstance(value, str) and value.lower() == 'ask':
|
||||||
return 'ask'
|
return 'ask'
|
||||||
else:
|
return super().from_py(value)
|
||||||
return super().transform(value)
|
|
||||||
|
|
||||||
def validate(self, value):
|
def from_str(self, value):
|
||||||
if value.lower() == 'ask':
|
return self.from_py(value)
|
||||||
return
|
|
||||||
else:
|
|
||||||
super().validate(value)
|
|
||||||
|
|
||||||
|
|
||||||
class Int(BaseType):
|
class Int(BaseType):
|
||||||
@ -470,26 +501,24 @@ class Int(BaseType):
|
|||||||
assert isinstance(value, int), value
|
assert isinstance(value, int), value
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def transform(self, value):
|
def from_str(self, value):
|
||||||
if not value:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return int(value)
|
|
||||||
|
|
||||||
def validate(self, value):
|
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
try:
|
try:
|
||||||
intval = int(value)
|
intval = int(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise configexc.ValidationError(value, "must be an integer!")
|
raise configexc.ValidationError(value, "must be an integer!")
|
||||||
if self.minval is not None and intval < self.minval:
|
return self.from_py(intval)
|
||||||
|
|
||||||
|
def from_py(self, value):
|
||||||
|
self._basic_validation(value, pytype=int)
|
||||||
|
if not value:
|
||||||
|
return value
|
||||||
|
if self.minval is not None and value < self.minval:
|
||||||
raise configexc.ValidationError(value, "must be {} or "
|
raise configexc.ValidationError(value, "must be {} or "
|
||||||
"bigger!".format(self.minval))
|
"bigger!".format(self.minval))
|
||||||
if self.maxval is not None and intval > self.maxval:
|
if self.maxval is not None and value > self.maxval:
|
||||||
raise configexc.ValidationError(value, "must be {} or "
|
raise configexc.ValidationError(value, "must be {} or "
|
||||||
"smaller!".format(self.maxval))
|
"smaller!".format(self.maxval))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class Float(BaseType):
|
class Float(BaseType):
|
||||||
@ -501,34 +530,46 @@ class Float(BaseType):
|
|||||||
maxval: Maximum value (inclusive).
|
maxval: Maximum value (inclusive).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# FIXME:conf inherit Int/Float
|
||||||
|
|
||||||
def __init__(self, minval=None, maxval=None, none_ok=False):
|
def __init__(self, minval=None, maxval=None, none_ok=False):
|
||||||
super().__init__(none_ok)
|
super().__init__(none_ok)
|
||||||
if maxval is not None and minval is not None and maxval < minval:
|
self.minval = self._parse_limit(minval)
|
||||||
raise ValueError("minval ({}) needs to be <= maxval ({})!".format(
|
self.maxval = self._parse_limit(maxval)
|
||||||
minval, maxval))
|
if self.maxval is not None and self.minval is not None:
|
||||||
self.minval = minval
|
if self.maxval < self.minval:
|
||||||
self.maxval = maxval
|
raise ValueError("minval ({}) needs to be <= maxval ({})!"
|
||||||
|
.format(self.minval, self.maxval))
|
||||||
|
|
||||||
def transform(self, value):
|
def _parse_limit(self, value):
|
||||||
if not value:
|
if value == 'maxint':
|
||||||
return None
|
return qtutils.MAXVALS['int']
|
||||||
|
elif value == 'maxint64':
|
||||||
|
return qtutils.MAXVALS['int64']
|
||||||
else:
|
else:
|
||||||
return float(value)
|
if value is not None:
|
||||||
|
assert isinstance(value, int), value
|
||||||
|
return value
|
||||||
|
|
||||||
def validate(self, value):
|
def from_str(self, value):
|
||||||
self._basic_validation(value)
|
|
||||||
if not value:
|
|
||||||
return
|
|
||||||
try:
|
try:
|
||||||
floatval = float(value)
|
intval = int(value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise configexc.ValidationError(value, "must be a float!")
|
raise configexc.ValidationError(value, "must be an integer!")
|
||||||
if self.minval is not None and floatval < self.minval:
|
return self.from_py(intval)
|
||||||
|
|
||||||
|
def from_py(self, value):
|
||||||
|
self._basic_validation(value, pytype=int)
|
||||||
|
if not value:
|
||||||
|
return value
|
||||||
|
if self.minval is not None and value < self.minval:
|
||||||
raise configexc.ValidationError(value, "must be {} or "
|
raise configexc.ValidationError(value, "must be {} or "
|
||||||
"bigger!".format(self.minval))
|
"bigger!".format(self.minval))
|
||||||
if self.maxval is not None and floatval > self.maxval:
|
if self.maxval is not None and value > self.maxval:
|
||||||
raise configexc.ValidationError(value, "must be {} or "
|
raise configexc.ValidationError(value, "must be {} or "
|
||||||
"smaller!".format(self.maxval))
|
"smaller!".format(self.maxval))
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Perc(BaseType):
|
class Perc(BaseType):
|
||||||
|
Loading…
Reference in New Issue
Block a user