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: * add SubMulti:
a SubMulti is a multi in a multi variable 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: * behavior change in master/slave part of code:
if slave has a default value greater than master's one, it's raise 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 # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
"enables us to carry out a calculation and return an option's value" "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.i18n import _
from tiramisu.setting import undefined 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 key, callbacks in callback_params.items():
for callbk in callbacks: for callbk in callbacks:
if isinstance(callbk, tuple): if isinstance(callbk, tuple):
if config is None:
raise ContextError()
if len(callbk) == 1: if len(callbk) == 1:
tcparams.setdefault(key, []).append((config, False)) tcparams.setdefault(key, []).append((config, False))
else: else:

View file

@ -34,6 +34,12 @@ class ConfigError(Exception):
pass pass
class ContextError(Exception):
"""context needed but not given
"""
pass
class ConflictError(Exception): class ConflictError(Exception):
"duplicate options are present in a single config" "duplicate options are present in a single config"
pass pass

View file

@ -500,7 +500,7 @@ class Option(OnlyOption):
validator_params = {} validator_params = {}
for val_param, values in self._validator_params.items(): for val_param, values in self._validator_params.items():
validator_params[val_param] = values validator_params[val_param] = values
#FIXME ... ca sert à quoi ... #inject value in calculation
if '' in validator_params: if '' in validator_params:
lst = list(validator_params['']) lst = list(validator_params[''])
lst.insert(0, val) lst.insert(0, val)
@ -519,7 +519,7 @@ class Option(OnlyOption):
return return
# option validation # option validation
try: try:
self._validate(_value) self._validate(_value, context)
except ValueError as err: except ValueError as err:
log.debug('do_validation: value: {0}, index: {1}, ' log.debug('do_validation: value: {0}, index: {1}, '
'submulti_index: {2}'.format(_value, _index, 'submulti_index: {2}'.format(_value, _index,

View file

@ -22,10 +22,13 @@
import re import re
import sys import sys
from IPy import IP 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 tiramisu.i18n import _
from .baseoption import Option from .baseoption import Option, validate_callback
from tiramisu.autolib import carry_out_calculation
class ChoiceOption(Option): class ChoiceOption(Option):
@ -35,20 +38,25 @@ class ChoiceOption(Option):
""" """
__slots__ = tuple() __slots__ = tuple()
def __init__(self, name, doc, values, default=None, default_multi=None, def __init__(self, name, doc, values, default=None,
requires=None, multi=False, callback=None, values_params=None, default_multi=None, requires=None,
callback_params=None, open_values=False, validator=None, multi=False, callback=None, callback_params=None,
validator_params=None, properties=None, warnings_only=False): 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 :param values: is a list of values the option can possibly take
""" """
if not isinstance(values, tuple): if isinstance(values, FunctionType):
raise TypeError(_('values must be a tuple for {0}').format(name)) 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): if open_values not in (True, False):
raise TypeError(_('open_values must be a boolean for ' raise TypeError(_('open_values must be a boolean for '
'{0}').format(name)) '{0}').format(name))
self._extra = {'_choice_open_values': open_values, 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, super(ChoiceOption, self).__init__(name, doc, default=default,
default_multi=default_multi, default_multi=default_multi,
callback=callback, callback=callback,
@ -60,24 +68,42 @@ class ChoiceOption(Option):
properties=properties, properties=properties,
warnings_only=warnings_only) warnings_only=warnings_only)
def impl_get_values(self): def impl_get_values(self, context):
return self._extra['_choice_values'] #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): def impl_is_openvalues(self):
return self._extra['_choice_open_values'] return self._extra['_choice_open_values']
def _validate(self, value): def _validate(self, value, context=None):
if not self.impl_is_openvalues() and not value in self.impl_get_values(): 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, ' raise ValueError(_('value {0} is not permitted, '
'only {1} is allowed' 'only {1} is allowed'
'').format(value, self._extra['_choice_values'])) '').format(value,
values))
except ContextError:
log.debug('ChoiceOption validation, disabled because no context')
class BoolOption(Option): class BoolOption(Option):
"represents a choice between ``True`` and ``False``" "represents a choice between ``True`` and ``False``"
__slots__ = tuple() __slots__ = tuple()
def _validate(self, value): def _validate(self, value, context=None):
if not isinstance(value, bool): if not isinstance(value, bool):
raise ValueError(_('invalid boolean')) raise ValueError(_('invalid boolean'))
@ -86,7 +112,7 @@ class IntOption(Option):
"represents a choice of an integer" "represents a choice of an integer"
__slots__ = tuple() __slots__ = tuple()
def _validate(self, value): def _validate(self, value, context=None):
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError(_('invalid integer')) raise ValueError(_('invalid integer'))
@ -95,7 +121,7 @@ class FloatOption(Option):
"represents a choice of a floating point number" "represents a choice of a floating point number"
__slots__ = tuple() __slots__ = tuple()
def _validate(self, value): def _validate(self, value, context=None):
if not isinstance(value, float): if not isinstance(value, float):
raise ValueError(_('invalid float')) raise ValueError(_('invalid float'))
@ -104,7 +130,7 @@ class StrOption(Option):
"represents the choice of a string" "represents the choice of a string"
__slots__ = tuple() __slots__ = tuple()
def _validate(self, value): def _validate(self, value, context=None):
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError(_('invalid string')) raise ValueError(_('invalid string'))
@ -120,7 +146,7 @@ else:
__slots__ = tuple() __slots__ = tuple()
_empty = u'' _empty = u''
def _validate(self, value): def _validate(self, value, context=None):
if not isinstance(value, unicode): if not isinstance(value, unicode):
raise ValueError(_('invalid unicode')) raise ValueError(_('invalid unicode'))
@ -147,7 +173,7 @@ class IPOption(Option):
properties=properties, properties=properties,
warnings_only=warnings_only) warnings_only=warnings_only)
def _validate(self, value): def _validate(self, value, context=None):
# sometimes an ip term starts with a zero # sometimes an ip term starts with a zero
# but this does not fit in some case, for example bind does not like it # but this does not fit in some case, for example bind does not like it
try: try:
@ -248,7 +274,7 @@ class PortOption(Option):
properties=properties, properties=properties,
warnings_only=warnings_only) warnings_only=warnings_only)
def _validate(self, value): def _validate(self, value, context=None):
if self._extra['_allow_range'] and ":" in str(value): if self._extra['_allow_range'] and ":" in str(value):
value = str(value).split(':') value = str(value).split(':')
if len(value) != 2: if len(value) != 2:
@ -275,7 +301,7 @@ class NetworkOption(Option):
"represents the choice of a network" "represents the choice of a network"
__slots__ = tuple() __slots__ = tuple()
def _validate(self, value): def _validate(self, value, context=None):
try: try:
IP(value) IP(value)
except ValueError: except ValueError:
@ -295,7 +321,7 @@ class NetmaskOption(Option):
"represents the choice of a netmask" "represents the choice of a netmask"
__slots__ = tuple() __slots__ = tuple()
def _validate(self, value): def _validate(self, value, context=None):
try: try:
IP('0.0.0.0/{0}'.format(value)) IP('0.0.0.0/{0}'.format(value))
except ValueError: except ValueError:
@ -344,7 +370,7 @@ class NetmaskOption(Option):
class BroadcastOption(Option): class BroadcastOption(Option):
__slots__ = tuple() __slots__ = tuple()
def _validate(self, value): def _validate(self, value, context=None):
try: try:
IP('{0}/32'.format(value)) IP('{0}/32'.format(value))
except ValueError: except ValueError:
@ -418,7 +444,7 @@ class DomainnameOption(Option):
properties=properties, properties=properties,
warnings_only=warnings_only) warnings_only=warnings_only)
def _validate(self, value): def _validate(self, value, context=None):
if self._extra['_allow_ip'] is True: if self._extra['_allow_ip'] is True:
try: try:
IP('{0}/32'.format(value)) IP('{0}/32'.format(value))
@ -440,7 +466,7 @@ class EmailOption(DomainnameOption):
__slots__ = tuple() __slots__ = tuple()
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$") username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
def _validate(self, value): def _validate(self, value, context=None):
splitted = value.split('@', 1) splitted = value.split('@', 1)
try: try:
username, domain = splitted username, domain = splitted
@ -457,7 +483,7 @@ class URLOption(DomainnameOption):
proto_re = re.compile(r'(http|https)://') proto_re = re.compile(r'(http|https)://')
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$") path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
def _validate(self, value): def _validate(self, value, context=None):
match = self.proto_re.search(value) match = self.proto_re.search(value)
if not match: if not match:
raise ValueError(_('invalid url, must start with http:// or ' raise ValueError(_('invalid url, must start with http:// or '
@ -493,7 +519,7 @@ class UsernameOption(Option):
#regexp build with 'man 8 adduser' informations #regexp build with 'man 8 adduser' informations
username_re = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$") 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) match = self.username_re.search(value)
if not match: if not match:
raise ValueError(_('invalid username')) raise ValueError(_('invalid username'))
@ -503,7 +529,7 @@ class FilenameOption(Option):
__slots__ = tuple() __slots__ = tuple()
path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$") 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) match = self.path_re.search(value)
if not match: if not match:
raise ValueError(_('invalid filename')) raise ValueError(_('invalid filename'))