NoValueReturn is not needed now + apply_requires is now in settings

This commit is contained in:
Emmanuel Garette 2013-04-08 16:05:56 +02:00
parent d8b68fa1ec
commit 67e67a5020
6 changed files with 134 additions and 144 deletions

View File

@ -108,7 +108,7 @@ def test_getpaths_with_hidden():
def test_str(): def test_str():
descr = make_description() descr = make_description()
c = Config(descr) c = Config(descr)
print c # does not crash c # does not crash
#def test_dir(): #def test_dir():
# descr = make_description() # descr = make_description()

View File

@ -23,8 +23,7 @@
#from inspect import getmembers, ismethod #from inspect import getmembers, ismethod
from tiramisu.error import (PropertiesOptionError, NotFoundError, from tiramisu.error import (PropertiesOptionError, NotFoundError,
AmbigousOptionError, NoMatchingOptionFound, MandatoryError) AmbigousOptionError, NoMatchingOptionFound, MandatoryError)
from tiramisu.option import (OptionDescription, Option, SymLinkOption, from tiramisu.option import OptionDescription, Option, SymLinkOption
apply_requires)
from tiramisu.setting import groups, Setting from tiramisu.setting import groups, Setting
from tiramisu.value import Values from tiramisu.value import Values
@ -78,7 +77,6 @@ class SubConfig(object):
def _validate(self, name, opt_or_descr, force_permissive=False): def _validate(self, name, opt_or_descr, force_permissive=False):
"validation for the setattr and the getattr" "validation for the setattr and the getattr"
apply_requires(opt_or_descr, self)
if not isinstance(opt_or_descr, Option) and \ if not isinstance(opt_or_descr, Option) and \
not isinstance(opt_or_descr, OptionDescription): not isinstance(opt_or_descr, OptionDescription):
raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr))) raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr)))
@ -87,7 +85,7 @@ class SubConfig(object):
properties = properties - set(['mandatory', 'frozen']) properties = properties - set(['mandatory', 'frozen'])
set_properties = set(self.cfgimpl_get_settings().get_properties()) set_properties = set(self.cfgimpl_get_settings().get_properties())
properties = properties & set_properties properties = properties & set_properties
if force_permissive is True or self.cfgimpl_get_settings().has_property('permissive'): if force_permissive is True or self.cfgimpl_get_settings().has_property('permissive', is_apply_req=False):
properties = properties - set(self.cfgimpl_get_settings().get_permissive()) properties = properties - set(self.cfgimpl_get_settings().get_permissive())
properties = properties - set(self.cfgimpl_get_settings().get_permissive(opt_or_descr)) properties = properties - set(self.cfgimpl_get_settings().get_permissive(opt_or_descr))
properties = list(properties) properties = list(properties)
@ -306,9 +304,13 @@ class SubConfig(object):
raise ValueError("make_dict can't filtering with value without option") raise ValueError("make_dict can't filtering with value without option")
if withoption is not None: if withoption is not None:
mypath = self.getpath() mypath = self.getpath()
for path in self.cfgimpl_get_context()._find(bytype=Option, byname=withoption, for path in self.cfgimpl_get_context()._find(bytype=Option,
byvalue=withvalue, byattrs=None, byname=withoption,
first=False, ret='path', _subpath=mypath): byvalue=withvalue,
byattrs=None,
first=False,
type_='path',
_subpath=mypath):
path = '.'.join(path.split('.')[:-1]) path = '.'.join(path.split('.')[:-1])
opt = self.cfgimpl_get_context().cfgimpl_get_description().get_opt_by_path(path) opt = self.cfgimpl_get_context().cfgimpl_get_description().get_opt_by_path(path)
if mypath is not None: if mypath is not None:
@ -362,7 +364,7 @@ class Config(SubConfig):
:param context: the current root config :param context: the current root config
:type context: `Config` :type context: `Config`
""" """
self._cfgimpl_settings = Setting() self._cfgimpl_settings = Setting(self)
self._cfgimpl_values = Values(self) self._cfgimpl_values = Values(self)
super(Config, self).__init__(descr, self) # , slots) super(Config, self).__init__(descr, self) # , slots)
self._cfgimpl_build_all_paths() self._cfgimpl_build_all_paths()
@ -390,6 +392,7 @@ class Config(SubConfig):
:param kwargs: dict of name strings to values. :param kwargs: dict of name strings to values.
""" """
#opts, paths = self.cfgimpl_get_description()._cache_paths
all_paths = [p.split(".") for p in self.getpaths(allpaths=True)] all_paths = [p.split(".") for p in self.getpaths(allpaths=True)]
for key, value in kwargs.iteritems(): for key, value in kwargs.iteritems():
key_p = key.split('.') key_p = key.split('.')

View File

@ -21,8 +21,6 @@ class RequirementRecursionError(RequiresError):
pass pass
class MandatoryError(Exception): class MandatoryError(Exception):
pass pass
class NoValueReturned(Exception):
pass
class OptionValueError(Exception): class OptionValueError(Exception):
pass pass
class MultiTypeError(Exception): class MultiTypeError(Exception):

View File

@ -24,10 +24,8 @@ import re
from copy import copy from copy import copy
from types import FunctionType from types import FunctionType
from tiramisu.error import (ConfigError, NotFoundError, ConflictConfigError, from tiramisu.error import (ConfigError, NotFoundError, ConflictConfigError,
RequiresError, RequirementRecursionError, RequiresError)
PropertiesOptionError) from tiramisu.setting import groups, multitypes, apply_requires
from tiramisu.autolib import carry_out_calculation
from tiramisu.setting import groups, multitypes
name_regexp = re.compile(r'^\d+') name_regexp = re.compile(r'^\d+')
@ -84,6 +82,7 @@ class Option(BaseInformation):
requires=None, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None, callback_params=None, validator=None, validator_args=None,
properties=None): properties=None):
#FIXME : validation de callback et callback_params !!!
""" """
:param name: the option's name :param name: the option's name
:param doc: the option's description :param doc: the option's description
@ -109,10 +108,11 @@ class Option(BaseInformation):
validate_requires_arg(requires, self._name) validate_requires_arg(requires, self._name)
self._requires = requires self._requires = requires
self.multi = multi self.multi = multi
#self._validator_args = None
if validator is not None: if validator is not None:
if type(validator) != FunctionType: if type(validator) != FunctionType:
raise TypeError("validator must be a function") raise TypeError("validator must be a function")
if validator_args is None:
validator_args = {}
self._validator = (validator, validator_args) self._validator = (validator, validator_args)
else: else:
self._validator = None self._validator = None
@ -139,14 +139,14 @@ class Option(BaseInformation):
raise ConfigError("invalid default value {0} " raise ConfigError("invalid default value {0} "
"for option {1} : not list type" "for option {1} : not list type"
"".format(str(default), name)) "".format(str(default), name))
if not self.validate(default, False): if not self.validate(default):
raise ConfigError("invalid default value {0} " raise ConfigError("invalid default value {0} "
"for option {1}" "for option {1}"
"".format(str(default), name)) "".format(str(default), name))
self.multitype = multitypes.default self.multitype = multitypes.default
self.default_multi = default_multi self.default_multi = default_multi
else: else:
if default is not None and not self.validate(default, False): if default is not None and not self.validate(default):
raise ConfigError("invalid default value {0} " raise ConfigError("invalid default value {0} "
"for option {1}".format(str(default), name)) "for option {1}".format(str(default), name))
self.default = default self.default = default
@ -216,14 +216,6 @@ class Option(BaseInformation):
else: else:
return True return True
def getcallback_value(self, config):
callback, callback_params = self.callback
if callback_params is None:
callback_params = {}
return carry_out_calculation(self._name, config=config,
callback=callback,
callback_params=callback_params)
def reset(self, config): def reset(self, config):
"""resets the default value and owner """resets the default value and owner
""" """
@ -558,54 +550,3 @@ def validate_requires_arg(requires, name):
" action: {1}".format(name, action)) " action: {1}".format(name, action))
else: else:
config_action[action] = inverse config_action[action] = inverse
def apply_requires(opt, config):
"carries out the jit (just in time requirements between options"
def build_actions(requires):
"action are hide, show, enable, disable..."
trigger_actions = {}
for require in requires:
action = require[2]
trigger_actions.setdefault(action, []).append(require)
return trigger_actions
#for symlink
if hasattr(opt, '_requires') and opt._requires is not None:
# filters the callbacks
setting = config.cfgimpl_get_settings()
trigger_actions = build_actions(opt._requires)
optpath = config.cfgimpl_get_context().cfgimpl_get_description().get_path_by_opt(opt)
for requires in trigger_actions.values():
matches = False
for require in requires:
if len(require) == 3:
path, expected, action = require
inverse = False
elif len(require) == 4:
path, expected, action, inverse = require
if path.startswith(optpath):
raise RequirementRecursionError("malformed requirements "
"imbrication detected for option: '{0}' "
"with requirement on: '{1}'".format(optpath, path))
try:
value = config.cfgimpl_get_context()._getattr(path, force_permissive=True)
except PropertiesOptionError, err:
properties = err.proptype
raise NotFoundError("option '{0}' has requirement's property error: "
"{1} {2}".format(opt._name, path, properties))
except Exception, err:
raise NotFoundError("required option not found: "
"{0}".format(path))
if value == expected:
if inverse:
setting.del_property(action, opt)
else:
setting.add_property(action, opt)
matches = True
#FIXME optimisation : fait un double break non ? voire un return
# no requirement has been triggered, then just reverse the action
if not matches:
if inverse:
setting.add_property(action, opt)
else:
setting.del_property(action, opt)

View File

@ -21,6 +21,9 @@
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.error import (RequirementRecursionError, PropertiesOptionError,
NotFoundError)
class _const: class _const:
"""convenient class that emulates a module """convenient class that emulates a module
@ -136,9 +139,9 @@ populate_multitypes()
#____________________________________________________________ #____________________________________________________________
class Setting(object): class Setting(object):
"``Config()``'s configuration options" "``Config()``'s configuration options"
__slots__ = ('properties', 'permissives', 'owner') __slots__ = ('properties', 'permissives', 'owner', 'context')
def __init__(self): def __init__(self, context):
# properties attribute: the name of a property enables this property # properties attribute: the name of a property enables this property
# key is None for global properties # key is None for global properties
self.properties = {None: []} # ['hidden', 'disabled', 'mandatory', 'frozen', 'validator']} self.properties = {None: []} # ['hidden', 'disabled', 'mandatory', 'frozen', 'validator']}
@ -146,24 +149,27 @@ class Setting(object):
self.permissives = {} self.permissives = {}
# generic owner # generic owner
self.owner = owners.user self.owner = owners.user
self.context = context
#____________________________________________________________ #____________________________________________________________
# properties methods # properties methods
def has_properties(self, opt=None): def has_properties(self, opt=None, is_apply_req=True):
"has properties means the Config's properties attribute is not empty" "has properties means the Config's properties attribute is not empty"
return bool(len(self.get_properties(opt))) return bool(len(self.get_properties(opt, is_apply_req)))
def get_properties(self, opt=None): def get_properties(self, opt=None, is_apply_req=True):
if opt is None: if opt is None:
default = [] default = []
else: else:
if is_apply_req:
apply_requires(opt, self.context)
default = list(opt._properties) default = list(opt._properties)
return self.properties.get(opt, default) return self.properties.get(opt, default)
def has_property(self, propname, opt=None): def has_property(self, propname, opt=None, is_apply_req=True):
"""has property propname in the Config's properties attribute """has property propname in the Config's properties attribute
:param property: string wich is the name of the property""" :param property: string wich is the name of the property"""
return propname in self.get_properties(opt) return propname in self.get_properties(opt, is_apply_req)
def enable_property(self, propname): def enable_property(self, propname):
"puts property propname in the Config's properties attribute" "puts property propname in the Config's properties attribute"
@ -192,14 +198,14 @@ class Setting(object):
else: else:
self.properties[opt] = properties self.properties[opt] = properties
def add_property(self, propname, opt): def add_property(self, propname, opt, is_apply_req=True):
properties = self.get_properties(opt) properties = self.get_properties(opt, is_apply_req)
if not propname in properties: if not propname in properties:
properties.append(propname) properties.append(propname)
self.set_properties(properties, opt) self.set_properties(properties, opt)
def del_property(self, propname, opt): def del_property(self, propname, opt, is_apply_req=True):
properties = self.get_properties(opt) properties = self.get_properties(opt, is_apply_req)
if propname in properties: if propname in properties:
properties.remove(propname) properties.remove(propname)
self.set_properties(properties, opt) self.set_properties(properties, opt)
@ -241,5 +247,56 @@ class Setting(object):
self.enable_property('hidden') self.enable_property('hidden')
self.enable_property('disabled') self.enable_property('disabled')
self.disable_property('mandatory') self.disable_property('mandatory')
self.disable_property('validator') self.enable_property('validator')
self.disable_property('permissive') self.disable_property('permissive')
def apply_requires(opt, config):
"carries out the jit (just in time requirements between options"
def build_actions(requires):
"action are hide, show, enable, disable..."
trigger_actions = {}
for require in requires:
action = require[2]
trigger_actions.setdefault(action, []).append(require)
return trigger_actions
#for symlink
if hasattr(opt, '_requires') and opt._requires is not None:
# filters the callbacks
setting = config.cfgimpl_get_settings()
trigger_actions = build_actions(opt._requires)
optpath = config.cfgimpl_get_context().cfgimpl_get_description().get_path_by_opt(opt)
for requires in trigger_actions.values():
matches = False
for require in requires:
if len(require) == 3:
path, expected, action = require
inverse = False
elif len(require) == 4:
path, expected, action, inverse = require
if path == optpath or path.startswith(optpath + '.'):
raise RequirementRecursionError("malformed requirements "
"imbrication detected for option: '{0}' "
"with requirement on: '{1}'".format(optpath, path))
try:
value = config.cfgimpl_get_context()._getattr(path, force_permissive=True)
except PropertiesOptionError, err:
properties = err.proptype
raise NotFoundError("option '{0}' has requirement's property error: "
"{1} {2}".format(opt._name, path, properties))
except AttributeError:
raise NotFoundError("required option not found: "
"{0}".format(path))
if value == expected:
if inverse:
setting.del_property(action, opt, False)
else:
setting.add_property(action, opt, False)
matches = True
#FIXME optimisation : fait un double break non ? voire un return
# no requirement has been triggered, then just reverse the action
if not matches:
if inverse:
setting.add_property(action, opt, False)
else:
setting.del_property(action, opt, False)

View File

@ -17,9 +17,10 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.error import NoValueReturned, MandatoryError, MultiTypeError, \ from tiramisu.error import MandatoryError, MultiTypeError, \
ConfigError # , OptionValueError ConfigError # , OptionValueError
from tiramisu.setting import owners, multitypes from tiramisu.setting import owners, multitypes
from tiramisu.autolib import carry_out_calculation
class Values(object): class Values(object):
@ -36,28 +37,35 @@ class Values(object):
self.context = context self.context = context
def _get_value(self, opt): def _get_value(self, opt):
"special case for the multis: they never return None" "return value or default value if not set"
#if no value
if opt not in self.values: if opt not in self.values:
value = opt.getdefault()
if opt.is_multi(): if opt.is_multi():
value = Multi(opt.getdefault(), self.context, opt) value = Multi(value, self.context, opt)
#if slave, had values until master's one
if opt.multitype == multitypes.slave: if opt.multitype == multitypes.slave:
masterpath = self.context._cfgimpl_descr.get_path_by_opt(opt.master_slaves) masterpath = self.context.cfgimpl_get_description().get_path_by_opt(opt.master_slaves)
mastervalue = getattr(self.context, masterpath) mastervalue = getattr(self.context, masterpath)
masterlen = len(mastervalue) masterlen = len(mastervalue)
if len(value) > masterlen:
raise MultiTypeError("invalid len for the slave: {0}"
" which has {1} as master".format(
opt._name, masterpath))
if len(value) < masterlen: if len(value) < masterlen:
for num in range(0, masterlen - len(value)): for num in range(0, masterlen - len(value)):
value.append(None, force=True) value.append(opt.getdefault_multi(), force=True)
else: #FIXME si inferieur ??
value = opt.getdefault() else:
#if value
return value value = self.values[opt][1]
return self.values[opt][1] return value
def reset(self, opt): def reset(self, opt):
if opt in self.values: if opt in self.values:
del(self.values[opt]) del(self.values[opt])
def _is_empty(self, opt, value=None): def _is_empty(self, opt, value):
"convenience method to know if an option is empty" "convenience method to know if an option is empty"
#FIXME: buggy ? #FIXME: buggy ?
#if value is not None: #if value is not None:
@ -68,22 +76,6 @@ class Values(object):
return True return True
return False return False
def is_empty(self, opt):
#FIXME that not empty ... just no value!
if opt not in self.values:
return True
value = self.values[opt][1]
if not opt.is_multi():
if self._get_value(opt) is None:
return True
return False
else:
value = list(value)
for val in value:
if val is not None:
return False
return True
def _test_mandatory(self, opt, value, force_properties=None): def _test_mandatory(self, opt, value, force_properties=None):
setting = self.context.cfgimpl_get_settings() setting = self.context.cfgimpl_get_settings()
if force_properties is None: if force_properties is None:
@ -92,8 +84,7 @@ class Values(object):
set_mandatory = ('mandatory' in force_properties or set_mandatory = ('mandatory' in force_properties or
setting.has_property('mandatory')) setting.has_property('mandatory'))
if setting.has_property('mandatory', opt) and set_mandatory: if setting.has_property('mandatory', opt) and set_mandatory:
if self._is_empty(opt, value) and \ if self._is_empty(opt, value) and opt.is_empty_by_default():
opt.is_empty_by_default():
raise MandatoryError("option: {0} is mandatory " raise MandatoryError("option: {0} is mandatory "
"and shall have a value".format(opt._name)) "and shall have a value".format(opt._name))
#empty value #empty value
@ -114,9 +105,16 @@ class Values(object):
_result = [result] _result = [result]
else: else:
_result = result _result = result
#multitype = self._get_multitype(opt)
return Multi(_result, self.context, opt) # , multitype) return Multi(_result, self.context, opt) # , multitype)
def _getcallback_value(self, opt):
callback, callback_params = opt.callback
if callback_params is None:
callback_params = {}
return carry_out_calculation(opt._name, config=self.context,
callback=callback,
callback_params=callback_params)
def __getitem__(self, opt): def __getitem__(self, opt):
return self._getitem(opt) return self._getitem(opt)
@ -124,32 +122,25 @@ class Values(object):
# options with callbacks # options with callbacks
value = self._get_value(opt) value = self._get_value(opt)
setting = self.context.cfgimpl_get_settings() setting = self.context.cfgimpl_get_settings()
is_frozen = setting.has_property('frozen', opt)
if opt.has_callback(): if opt.has_callback():
is_frozen = setting.has_property('frozen', opt) #if value is set and :
if (not is_frozen or (is_frozen and # - not frozen
not setting.has_property('force_default_on_freeze', opt) # - frozen and not force_default_on_freeze
)) and not self.context.cfgimpl_get_values().is_default_owner(opt): if not self.is_default_owner(opt) and (
not is_frozen or (is_frozen and
not setting.has_property('force_default_on_freeze', opt))):
return value return value
try: value = self._getcallback_value(opt)
result = opt.getcallback_value(self.context) if opt.is_multi():
except NoValueReturned: value = self.fill_multi(opt, value)
pass if not opt.validate(value, setting.has_property('validator')):
else: raise ConfigError('invalid calculated value returned'
if opt.is_multi(): ' for option {0}: {1}'.format(opt._name, value))
value = self.fill_multi(opt, result) #suppress value if already set
else: self.reset(opt)
# this result **shall not** be a list
if isinstance(result, list):
raise ConfigError('invalid calculated value returned '
'for option {0} : shall not be a list'
''.format(opt._name))
value = result
if value is not None and \
not opt.validate(value, setting.has_property('validator')):
raise ConfigError('invalid calculated value returned'
' for option {0}'.format(opt._name))
# frozen and force default # frozen and force default
elif setting.has_property('force_default_on_freeze', opt): elif is_frozen and setting.has_property('force_default_on_freeze', opt):
value = opt.getdefault() value = opt.getdefault()
if opt.is_multi(): if opt.is_multi():
value = self.fill_multi(opt, value) value = self.fill_multi(opt, value)
@ -168,7 +159,7 @@ class Values(object):
slave._name, opt._name)) slave._name, opt._name))
elif len(value_slave) < masterlen: elif len(value_slave) < masterlen:
for num in range(0, masterlen - len(value_slave)): for num in range(0, masterlen - len(value_slave)):
value_slave.append(None, force=True) value_slave.append(slave.getdefault_multi(), force=True)
elif opt.multitype == multitypes.slave: elif opt.multitype == multitypes.slave:
if len(self._get_value(opt.master_slaves)) != len(value): if len(self._get_value(opt.master_slaves)) != len(value):
@ -241,7 +232,7 @@ class Multi(list):
" which is a slave".format(self.opt._name)) " which is a slave".format(self.opt._name))
elif self.opt.multitype == multitypes.master: elif self.opt.multitype == multitypes.master:
for slave in self.opt.master_slaves: for slave in self.opt.master_slaves:
self.context.cfgimpl_get_values()[slave].append(None, force=True) self.context.cfgimpl_get_values()[slave].append(slave.getdefault_multi(), force=True)
self._validate(value) self._validate(value)
self.context.cfgimpl_get_values().setitem(self.opt, self) self.context.cfgimpl_get_values().setitem(self.opt, self)
super(Multi, self).append(value) super(Multi, self).append(value)