Values in ChoiceOption can be a function now
This commit is contained in:
parent
9112a8c5b0
commit
31fff062e1
5 changed files with 76 additions and 36 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,9 +1,15 @@
|
|||
XXXXXXXXXXXXX Emmanuel Garette <egarette@cadoles.com>
|
||||
Sun Apr 27 10:32:40 2014 +0200 Emmanuel Garette <egarette@cadoles.com>
|
||||
|
||||
* add dynamic ChoiceOption:
|
||||
we can have dynamic ChoiceOption. Parameter values can be a function
|
||||
and as callback, we can add values_params
|
||||
|
||||
Fri Apr 25 22:57:08 2014 +0200 Emmanuel Garette <egarette@cadoles.com>
|
||||
|
||||
* add SubMulti:
|
||||
a SubMulti is a multi in a multi variable
|
||||
|
||||
Sat Apr 12 11:37:27 CEST 2014 Emmanuel Garette <egarette@cadoles.com>
|
||||
Sat Apr 12 11:37:27 2014 +0200 Emmanuel Garette <egarette@cadoles.com>
|
||||
|
||||
* behavior change in master/slave part of code:
|
||||
if slave has a default value greater than master's one, it's raise
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
"enables us to carry out a calculation and return an option's value"
|
||||
from tiramisu.error import PropertiesOptionError, ConfigError
|
||||
from tiramisu.error import PropertiesOptionError, ConfigError, ContextError
|
||||
from tiramisu.i18n import _
|
||||
from tiramisu.setting import undefined
|
||||
# ____________________________________________________________
|
||||
|
@ -143,6 +143,8 @@ def carry_out_calculation(option, config, callback, callback_params,
|
|||
for key, callbacks in callback_params.items():
|
||||
for callbk in callbacks:
|
||||
if isinstance(callbk, tuple):
|
||||
if config is None:
|
||||
raise ContextError()
|
||||
if len(callbk) == 1:
|
||||
tcparams.setdefault(key, []).append((config, False))
|
||||
else:
|
||||
|
|
|
@ -34,6 +34,12 @@ class ConfigError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class ContextError(Exception):
|
||||
"""context needed but not given
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ConflictError(Exception):
|
||||
"duplicate options are present in a single config"
|
||||
pass
|
||||
|
|
|
@ -500,7 +500,7 @@ class Option(OnlyOption):
|
|||
validator_params = {}
|
||||
for val_param, values in self._validator_params.items():
|
||||
validator_params[val_param] = values
|
||||
#FIXME ... ca sert à quoi ...
|
||||
#inject value in calculation
|
||||
if '' in validator_params:
|
||||
lst = list(validator_params[''])
|
||||
lst.insert(0, val)
|
||||
|
@ -519,7 +519,7 @@ class Option(OnlyOption):
|
|||
return
|
||||
# option validation
|
||||
try:
|
||||
self._validate(_value)
|
||||
self._validate(_value, context)
|
||||
except ValueError as err:
|
||||
log.debug('do_validation: value: {0}, index: {1}, '
|
||||
'submulti_index: {2}'.format(_value, _index,
|
||||
|
|
|
@ -22,10 +22,13 @@
|
|||
import re
|
||||
import sys
|
||||
from IPy import IP
|
||||
from types import FunctionType
|
||||
from tiramisu.setting import log
|
||||
|
||||
from tiramisu.error import ConfigError
|
||||
from tiramisu.error import ConfigError, ContextError
|
||||
from tiramisu.i18n import _
|
||||
from .baseoption import Option
|
||||
from .baseoption import Option, validate_callback
|
||||
from tiramisu.autolib import carry_out_calculation
|
||||
|
||||
|
||||
class ChoiceOption(Option):
|
||||
|
@ -35,20 +38,25 @@ class ChoiceOption(Option):
|
|||
"""
|
||||
__slots__ = tuple()
|
||||
|
||||
def __init__(self, name, doc, values, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
callback_params=None, open_values=False, validator=None,
|
||||
validator_params=None, properties=None, warnings_only=False):
|
||||
def __init__(self, name, doc, values, default=None,
|
||||
values_params=None, default_multi=None, requires=None,
|
||||
multi=False, callback=None, callback_params=None,
|
||||
open_values=False, validator=None, validator_params=None,
|
||||
properties=None, warnings_only=False):
|
||||
"""
|
||||
:param values: is a list of values the option can possibly take
|
||||
"""
|
||||
if not isinstance(values, tuple):
|
||||
raise TypeError(_('values must be a tuple for {0}').format(name))
|
||||
if isinstance(values, FunctionType):
|
||||
validate_callback(values, values_params, 'values')
|
||||
elif not isinstance(values, tuple):
|
||||
raise TypeError(_('values must be a tuple or a function for {0}'
|
||||
).format(name))
|
||||
if open_values not in (True, False):
|
||||
raise TypeError(_('open_values must be a boolean for '
|
||||
'{0}').format(name))
|
||||
self._extra = {'_choice_open_values': open_values,
|
||||
'_choice_values': values}
|
||||
'_choice_values': values,
|
||||
'_choice_values_params': values_params}
|
||||
super(ChoiceOption, self).__init__(name, doc, default=default,
|
||||
default_multi=default_multi,
|
||||
callback=callback,
|
||||
|
@ -60,24 +68,42 @@ class ChoiceOption(Option):
|
|||
properties=properties,
|
||||
warnings_only=warnings_only)
|
||||
|
||||
def impl_get_values(self):
|
||||
return self._extra['_choice_values']
|
||||
def impl_get_values(self, context):
|
||||
#FIXME cache? but in context...
|
||||
values = self._extra['_choice_values']
|
||||
if isinstance(values, FunctionType):
|
||||
values_params = self._extra['_choice_values_params']
|
||||
if values_params is None:
|
||||
values_params = {}
|
||||
values = carry_out_calculation(self, config=context,
|
||||
callback=values,
|
||||
callback_params=values_params)
|
||||
if not isinstance(values, list):
|
||||
raise ConfigError(_('calculated values for {0} is not a list'
|
||||
'').format(self.impl_getname()))
|
||||
return values
|
||||
|
||||
def impl_is_openvalues(self):
|
||||
return self._extra['_choice_open_values']
|
||||
|
||||
def _validate(self, value):
|
||||
if not self.impl_is_openvalues() and not value in self.impl_get_values():
|
||||
raise ValueError(_('value {0} is not permitted, '
|
||||
'only {1} is allowed'
|
||||
'').format(value, self._extra['_choice_values']))
|
||||
def _validate(self, value, context=None):
|
||||
try:
|
||||
values = self.impl_get_values(context)
|
||||
if not self.impl_is_openvalues() and \
|
||||
not value in values:
|
||||
raise ValueError(_('value {0} is not permitted, '
|
||||
'only {1} is allowed'
|
||||
'').format(value,
|
||||
values))
|
||||
except ContextError:
|
||||
log.debug('ChoiceOption validation, disabled because no context')
|
||||
|
||||
|
||||
class BoolOption(Option):
|
||||
"represents a choice between ``True`` and ``False``"
|
||||
__slots__ = tuple()
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
if not isinstance(value, bool):
|
||||
raise ValueError(_('invalid boolean'))
|
||||
|
||||
|
@ -86,7 +112,7 @@ class IntOption(Option):
|
|||
"represents a choice of an integer"
|
||||
__slots__ = tuple()
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
if not isinstance(value, int):
|
||||
raise ValueError(_('invalid integer'))
|
||||
|
||||
|
@ -95,7 +121,7 @@ class FloatOption(Option):
|
|||
"represents a choice of a floating point number"
|
||||
__slots__ = tuple()
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
if not isinstance(value, float):
|
||||
raise ValueError(_('invalid float'))
|
||||
|
||||
|
@ -104,7 +130,7 @@ class StrOption(Option):
|
|||
"represents the choice of a string"
|
||||
__slots__ = tuple()
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
if not isinstance(value, str):
|
||||
raise ValueError(_('invalid string'))
|
||||
|
||||
|
@ -120,7 +146,7 @@ else:
|
|||
__slots__ = tuple()
|
||||
_empty = u''
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
if not isinstance(value, unicode):
|
||||
raise ValueError(_('invalid unicode'))
|
||||
|
||||
|
@ -147,7 +173,7 @@ class IPOption(Option):
|
|||
properties=properties,
|
||||
warnings_only=warnings_only)
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
# sometimes an ip term starts with a zero
|
||||
# but this does not fit in some case, for example bind does not like it
|
||||
try:
|
||||
|
@ -248,7 +274,7 @@ class PortOption(Option):
|
|||
properties=properties,
|
||||
warnings_only=warnings_only)
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
if self._extra['_allow_range'] and ":" in str(value):
|
||||
value = str(value).split(':')
|
||||
if len(value) != 2:
|
||||
|
@ -275,7 +301,7 @@ class NetworkOption(Option):
|
|||
"represents the choice of a network"
|
||||
__slots__ = tuple()
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
try:
|
||||
IP(value)
|
||||
except ValueError:
|
||||
|
@ -295,7 +321,7 @@ class NetmaskOption(Option):
|
|||
"represents the choice of a netmask"
|
||||
__slots__ = tuple()
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
try:
|
||||
IP('0.0.0.0/{0}'.format(value))
|
||||
except ValueError:
|
||||
|
@ -344,7 +370,7 @@ class NetmaskOption(Option):
|
|||
class BroadcastOption(Option):
|
||||
__slots__ = tuple()
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
try:
|
||||
IP('{0}/32'.format(value))
|
||||
except ValueError:
|
||||
|
@ -418,7 +444,7 @@ class DomainnameOption(Option):
|
|||
properties=properties,
|
||||
warnings_only=warnings_only)
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
if self._extra['_allow_ip'] is True:
|
||||
try:
|
||||
IP('{0}/32'.format(value))
|
||||
|
@ -440,7 +466,7 @@ class EmailOption(DomainnameOption):
|
|||
__slots__ = tuple()
|
||||
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
splitted = value.split('@', 1)
|
||||
try:
|
||||
username, domain = splitted
|
||||
|
@ -457,7 +483,7 @@ class URLOption(DomainnameOption):
|
|||
proto_re = re.compile(r'(http|https)://')
|
||||
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
match = self.proto_re.search(value)
|
||||
if not match:
|
||||
raise ValueError(_('invalid url, must start with http:// or '
|
||||
|
@ -493,7 +519,7 @@ class UsernameOption(Option):
|
|||
#regexp build with 'man 8 adduser' informations
|
||||
username_re = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
match = self.username_re.search(value)
|
||||
if not match:
|
||||
raise ValueError(_('invalid username'))
|
||||
|
@ -503,7 +529,7 @@ class FilenameOption(Option):
|
|||
__slots__ = tuple()
|
||||
path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
|
||||
|
||||
def _validate(self, value):
|
||||
def _validate(self, value, context=None):
|
||||
match = self.path_re.search(value)
|
||||
if not match:
|
||||
raise ValueError(_('invalid filename'))
|
||||
|
|
Loading…
Reference in a new issue