add global 'empty' property, this property raise mandatory PropertiesOptionError if multi or master have empty value

This commit is contained in:
Emmanuel Garette 2015-07-26 18:55:21 +02:00
parent 1da83edfba
commit 8e7a32de08
7 changed files with 145 additions and 22 deletions

View File

@ -1,3 +1,7 @@
Sun Jul 26 19:09:29 2015 +0200 Emmanuel Garette <egarette@cadoles.com>
* add global 'empty' property, this property raise mandatory
PropertiesOptionError if multi or master have empty value
Fri Jul 24 18:03:59 2015 +0200 Emmanuel Garette <egarette@cadoles.com> Fri Jul 24 18:03:59 2015 +0200 Emmanuel Garette <egarette@cadoles.com>
* add duplicate option to Config, to generate new Config with same * add duplicate option to Config, to generate new Config with same
value, properties, Option. Option are not duplication. value, properties, Option. Option are not duplication.

View File

@ -1,12 +1,13 @@
# coding: utf-8
from autopath import do_autopath from autopath import do_autopath
do_autopath() do_autopath()
from time import sleep from time import sleep
#from py.test import raises from py.test import raises
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import StrOption, UnicodeOption, OptionDescription from tiramisu.option import StrOption, UnicodeOption, OptionDescription
from tiramisu.error import PropertiesOptionError from tiramisu.error import PropertiesOptionError
from tiramisu.setting import groups
def make_description(): def make_description():
@ -316,3 +317,87 @@ def test_mandatory_warnings_frozen():
config.read_only() config.read_only()
assert config.cfgimpl_get_values().mandatory_warnings() == ['str', 'str1', 'unicode2', 'str3'] assert config.cfgimpl_get_values().mandatory_warnings() == ['str', 'str1', 'unicode2', 'str3']
sleep(.1) sleep(.1)
def test_mandatory_master():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True,
properties=('mandatory', ))
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau",
multi=True)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
o = OptionDescription('o', '', [interface1])
config = Config(o)
config.read_only()
raises(PropertiesOptionError, 'config.ip_admin_eth0.ip_admin_eth0')
raises(PropertiesOptionError, 'config.ip_admin_eth0.netmask_admin_eth0')
def test_mandatory_master_empty():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau",
multi=True)
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
o = OptionDescription('o', '', [interface1])
config = Config(o)
config.read_write()
assert config.ip_admin_eth0.ip_admin_eth0 == []
assert config.ip_admin_eth0.netmask_admin_eth0 == []
#
config.ip_admin_eth0.ip_admin_eth0.append()
assert config.ip_admin_eth0.ip_admin_eth0 == [None]
assert config.ip_admin_eth0.netmask_admin_eth0 == [None]
config.read_only()
raises(PropertiesOptionError, "config.ip_admin_eth0.ip_admin_eth0")
raises(PropertiesOptionError, "config.ip_admin_eth0.netmask_admin_eth0")
config.read_write()
del(config.ip_admin_eth0.ip_admin_eth0)
del(config.ip_admin_eth0.netmask_admin_eth0)
assert config.ip_admin_eth0.ip_admin_eth0 == []
assert config.ip_admin_eth0.netmask_admin_eth0 == []
#
config.ip_admin_eth0.ip_admin_eth0.append('')
assert config.ip_admin_eth0.ip_admin_eth0 == ['']
assert config.ip_admin_eth0.netmask_admin_eth0 == [None]
config.read_only()
raises(PropertiesOptionError, "config.ip_admin_eth0.ip_admin_eth0")
raises(PropertiesOptionError, "config.ip_admin_eth0.netmask_admin_eth0")
config.read_write()
#
config.read_write()
config.ip_admin_eth0.ip_admin_eth0 = ['ip']
config.read_only()
assert config.ip_admin_eth0.ip_admin_eth0 == ['ip']
assert config.ip_admin_eth0.netmask_admin_eth0 == [None]
def test_mandatory_slave():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau",
multi=True, properties=('mandatory', ))
interface1 = OptionDescription('ip_admin_eth0', '', [ip_admin_eth0, netmask_admin_eth0])
interface1.impl_set_group_type(groups.master)
o = OptionDescription('o', '', [interface1])
config = Config(o)
config.read_only()
assert config.ip_admin_eth0.ip_admin_eth0 == []
assert config.ip_admin_eth0.netmask_admin_eth0 == []
#
config.read_write()
config.ip_admin_eth0.ip_admin_eth0.append('ip')
config.read_only()
assert config.ip_admin_eth0.ip_admin_eth0 == ['ip']
raises(PropertiesOptionError, 'config.ip_admin_eth0.netmask_admin_eth0')
#
config.read_write()
config.ip_admin_eth0.netmask_admin_eth0 = ['']
config.read_only()
assert config.ip_admin_eth0.ip_admin_eth0 == ['ip']
raises(PropertiesOptionError, 'config.ip_admin_eth0.netmask_admin_eth0')
#
config.read_write()
config.ip_admin_eth0.netmask_admin_eth0 = ['ip']
config.read_only()
assert config.ip_admin_eth0.ip_admin_eth0 == ['ip']
assert config.ip_admin_eth0.netmask_admin_eth0 == ['ip']

View File

@ -3,9 +3,9 @@ from autopath import do_autopath
do_autopath() do_autopath()
from tiramisu.value import Multi from tiramisu.value import Multi
from tiramisu.option import IntOption, OptionDescription from tiramisu.option import IntOption, StrOption, OptionDescription
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.error import ConfigError from tiramisu.error import ConfigError, PropertiesOptionError
import weakref import weakref
from py.test import raises from py.test import raises
@ -21,3 +21,21 @@ def test_multi():
assert c is multi._getcontext() assert c is multi._getcontext()
del(c) del(c)
raises(ConfigError, "multi._getcontext()") raises(ConfigError, "multi._getcontext()")
def test_multi_none():
s = StrOption('str', '', multi=True)
o = OptionDescription('od', '', [s])
c = Config(o)
c.read_only()
assert c.str == []
c.read_write()
c.str.append(None)
assert c.str == [None]
c.read_only()
raises(PropertiesOptionError, "c.str")
c.read_write()
c.str = ['']
assert c.str == ['']
c.read_only()
raises(PropertiesOptionError, "c.str")

View File

@ -99,7 +99,7 @@ class Base(StorageBase):
def __init__(self, name, doc, default=None, default_multi=None, def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_params=None, callback_params=None, validator=None, validator_params=None,
properties=None, warnings_only=False, extra=None, allow_empty_list=False): properties=None, warnings_only=False, extra=None, allow_empty_list=undefined):
if not valid_name(name): # pragma: optional cover if not valid_name(name): # pragma: optional cover
raise ValueError(_("invalid name: {0} for option").format(name)) raise ValueError(_("invalid name: {0} for option").format(name))
if requires is not None: if requires is not None:
@ -900,7 +900,7 @@ class SymLinkOption(OnlyOption):
'for symlink {0}').format(name)) 'for symlink {0}').format(name))
super(Base, self).__init__(name, undefined, undefined, undefined, super(Base, self).__init__(name, undefined, undefined, undefined,
undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined,
False, opt) undefined, opt)
self.commit() self.commit()
def __getattr__(self, name, context=undefined): def __getattr__(self, name, context=undefined):

View File

@ -75,6 +75,9 @@ everything_frozen
whole option in config are frozen (even if option have not frozen whole option in config are frozen (even if option have not frozen
property) property)
empty
raise mandatory PropertiesOptionError if multi or master have empty value
validator validator
launch validator set by user in option (this property has no effect launch validator set by user in option (this property has no effect
for internal validator) for internal validator)
@ -97,10 +100,10 @@ read_write
you can set all variables not frozen you can set all variables not frozen
""" """
ro_append = set(['frozen', 'disabled', 'validator', 'everything_frozen', ro_append = set(['frozen', 'disabled', 'validator', 'everything_frozen',
'mandatory']) 'mandatory', 'empty'])
ro_remove = set(['permissive', 'hidden']) ro_remove = set(['permissive', 'hidden'])
rw_append = set(['frozen', 'disabled', 'validator', 'hidden']) rw_append = set(['frozen', 'disabled', 'validator', 'hidden'])
rw_remove = set(['permissive', 'everything_frozen', 'mandatory']) rw_remove = set(['permissive', 'everything_frozen', 'mandatory', 'empty'])
forbidden_set_properties = set(['force_store_value']) forbidden_set_properties = set(['force_store_value'])
@ -468,8 +471,13 @@ class Settings(object):
else: else:
if 'mandatory' in properties and \ if 'mandatory' in properties and \
not self._getcontext().cfgimpl_get_values()._isempty( not self._getcontext().cfgimpl_get_values()._isempty(
opt_or_descr, value, opt_or_descr.impl_allow_empty_list()): opt_or_descr, value):
properties.remove('mandatory') properties.remove('mandatory')
elif not is_write and 'empty' in forced_properties and \
not opt_or_descr.impl_is_master_slaves('slave') and \
self._getcontext().cfgimpl_get_values()._isempty(
opt_or_descr, value, force_allow_empty_list=True):
properties.add('mandatory')
if is_write and 'everything_frozen' in forced_properties: if is_write and 'everything_frozen' in forced_properties:
properties.add('frozen') properties.add('frozen')
elif 'frozen' in properties and not is_write: elif 'frozen' in properties and not is_write:

View File

@ -79,7 +79,7 @@ class StorageBase(object):
self._properties = properties self._properties = properties
if opt is not undefined: if opt is not undefined:
self._opt = opt self._opt = opt
if allow_empty_list is not False: if allow_empty_list is not undefined:
self._allow_empty_list = allow_empty_list self._allow_empty_list = allow_empty_list
def _set_default_values(self, default, default_multi): def _set_default_values(self, default, default_multi):
@ -302,8 +302,7 @@ class StorageBase(object):
try: try:
return self._allow_empty_list return self._allow_empty_list
except AttributeError: except AttributeError:
return False return undefined
def _get_extra(self, key): def _get_extra(self, key):
extra = self._extra extra = self._extra

View File

@ -175,18 +175,27 @@ class Values(object):
if hasvalue: if hasvalue:
self._p_.resetvalue(path) self._p_.resetvalue(path)
def _isempty(self, opt, value, allow_empty_list): def _isempty(self, opt, value, force_allow_empty_list=False):
"convenience method to know if an option is empty" "convenience method to know if an option is empty"
empty = opt._empty if value is undefined:
if value is not undefined: return False
empty_not_multi = not opt.impl_is_multi() and (value is None or
value == empty)
empty_multi = opt.impl_is_multi() and ((not allow_empty_list and value == []) or
None in value or
empty in value)
else: else:
empty_multi = empty_not_multi = False empty = opt._empty
return empty_not_multi or empty_multi if opt.impl_is_multi():
if force_allow_empty_list:
allow_empty_list = True
else:
allow_empty_list = opt.impl_allow_empty_list()
if allow_empty_list is undefined:
if opt.impl_is_master_slaves('slave'):
allow_empty_list = True
else:
allow_empty_list = False
isempty = (not allow_empty_list and value == []) or \
None in value or empty in value
else:
isempty = value is None or value == empty
return isempty
def __getitem__(self, opt): def __getitem__(self, opt):
"enables us to use the pythonic dictionary-like access to values" "enables us to use the pythonic dictionary-like access to values"