Values in ChoiceOption can be a function now

This commit is contained in:
Emmanuel Garette 2014-04-27 10:32:40 +02:00
parent 9112a8c5b0
commit 31fff062e1
5 changed files with 76 additions and 36 deletions

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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,

View file

@ -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'))