mandatory is a true property (no more MandatoryError) + tests
This commit is contained in:
parent
6097f3af84
commit
656b751995
7 changed files with 293 additions and 92 deletions
|
@ -106,15 +106,3 @@ def test_cfgimpl_get_home_by_path():
|
|||
assert config.getpaths(include_groups=False) == ['gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
|
||||
assert config.getpaths(include_groups=True) == ['gc', 'gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
|
||||
|
||||
def test_mandatory_warnings():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
assert(MandatoryError, "config.str = ''")
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_write()
|
||||
assert list(mandatory_warnings(config)) == []
|
||||
setting.disable_property('mandatory')
|
||||
config.str = ''
|
||||
assert list(mandatory_warnings(config)) == ['str']
|
||||
setting.enable_property('mandatory')
|
||||
assert list(mandatory_warnings(config)) == ['str']
|
||||
|
|
214
test/test_mandatory.py
Normal file
214
test/test_mandatory.py
Normal file
|
@ -0,0 +1,214 @@
|
|||
import autopath
|
||||
|
||||
#from py.test import raises
|
||||
from tiramisu.config import Config, mandatory_warnings
|
||||
from tiramisu.option import StrOption, OptionDescription
|
||||
from tiramisu.error import PropertiesOptionError
|
||||
|
||||
|
||||
def make_description():
|
||||
stroption = StrOption('str', 'Test string option', default="abc",
|
||||
properties=('mandatory', ))
|
||||
stroption1 = StrOption('str1', 'Test string option',
|
||||
properties=('mandatory', ))
|
||||
stroption2 = StrOption('str2', 'Test string option',
|
||||
properties=('mandatory', ))
|
||||
stroption3 = StrOption('str3', 'Test string option', multi=True,
|
||||
properties=('mandatory', ))
|
||||
descr = OptionDescription('tiram', '', [stroption, stroption1, stroption2, stroption3])
|
||||
return descr
|
||||
|
||||
|
||||
def test_mandatory_ro():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_only()
|
||||
prop = []
|
||||
try:
|
||||
config.str1
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
setting.read_write()
|
||||
config.str1 = 'yes'
|
||||
setting.read_only()
|
||||
assert config.str1 == 'yes'
|
||||
|
||||
|
||||
def test_mandatory_rw():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_write()
|
||||
#not mandatory in rw
|
||||
config.str2
|
||||
config.str2 = 'yes'
|
||||
assert config.str2 == 'yes'
|
||||
|
||||
|
||||
def test_mandatory_default():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_only()
|
||||
#not mandatory in rw
|
||||
config.str
|
||||
setting.read_write()
|
||||
config.str = 'yes'
|
||||
setting.read_only()
|
||||
config.str
|
||||
setting.read_write()
|
||||
config.str = None
|
||||
setting.read_only()
|
||||
prop = []
|
||||
try:
|
||||
config.str
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
|
||||
|
||||
#valeur vide : None, '', u'', ...
|
||||
def test_mandatory_none():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
config.str1 = None
|
||||
setting = config.cfgimpl_get_settings()
|
||||
assert config.cfgimpl_get_values().getowner(descr.str1) == 'user'
|
||||
setting.read_only()
|
||||
prop = []
|
||||
try:
|
||||
config.str1
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
|
||||
|
||||
def test_mandatory_empty():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
config.str1 = ''
|
||||
setting = config.cfgimpl_get_settings()
|
||||
assert config.cfgimpl_get_values().getowner(descr.str1) == 'user'
|
||||
setting.read_only()
|
||||
prop = []
|
||||
try:
|
||||
config.str1
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
|
||||
|
||||
def test_mandatory_multi_none():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
setting = config.cfgimpl_get_settings()
|
||||
config.str3 = [None]
|
||||
setting.read_only()
|
||||
assert config.cfgimpl_get_values().getowner(descr.str3) == 'user'
|
||||
prop = []
|
||||
try:
|
||||
config.str3
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
setting.read_write()
|
||||
config.str3 = ['yes', None]
|
||||
setting.read_only()
|
||||
assert config.cfgimpl_get_values().getowner(descr.str3) == 'user'
|
||||
prop = []
|
||||
try:
|
||||
config.str3
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
|
||||
|
||||
def test_mandatory_multi_empty():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
setting = config.cfgimpl_get_settings()
|
||||
config.str3 = ['']
|
||||
setting.read_only()
|
||||
assert config.cfgimpl_get_values().getowner(descr.str3) == 'user'
|
||||
prop = []
|
||||
try:
|
||||
config.str3
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
setting.read_write()
|
||||
config.str3 = ['yes', '']
|
||||
setting.read_only()
|
||||
assert config.cfgimpl_get_values().getowner(descr.str3) == 'user'
|
||||
prop = []
|
||||
try:
|
||||
config.str3
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert 'mandatory' in prop
|
||||
|
||||
|
||||
def test_mandatory_disabled():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
setting = config.cfgimpl_get_settings()
|
||||
config.str1
|
||||
setting.read_only()
|
||||
prop = []
|
||||
try:
|
||||
config.str1
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert prop == ['mandatory']
|
||||
setting.add_property('disabled', descr.str1)
|
||||
prop = []
|
||||
try:
|
||||
config.str1
|
||||
except PropertiesOptionError, err:
|
||||
prop = err.proptype
|
||||
assert prop == ['disabled', 'mandatory']
|
||||
|
||||
|
||||
def test_mandatory_warnings_ro():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
config.str = ''
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_only()
|
||||
proc = []
|
||||
try:
|
||||
config.str
|
||||
except PropertiesOptionError, err:
|
||||
proc = err.proptype
|
||||
assert proc == ['mandatory']
|
||||
assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3']
|
||||
setting.read_write()
|
||||
config.str = 'a'
|
||||
setting.read_only()
|
||||
assert list(mandatory_warnings(config)) == ['str1', 'str2', 'str3']
|
||||
|
||||
|
||||
def test_mandatory_warnings_rw():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
config.str = ''
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_write()
|
||||
config.str
|
||||
assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3']
|
||||
config.str = 'a'
|
||||
assert list(mandatory_warnings(config)) == ['str1', 'str2', 'str3']
|
||||
|
||||
|
||||
def test_mandatory_warnings_disabled():
|
||||
descr = make_description()
|
||||
config = Config(descr)
|
||||
config.str = ''
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_write()
|
||||
config.str
|
||||
assert list(mandatory_warnings(config)) == ['str', 'str1', 'str2', 'str3']
|
||||
setting.add_property('disabled', descr.str)
|
||||
assert list(mandatory_warnings(config)) == ['str1', 'str2', 'str3']
|
|
@ -4,7 +4,7 @@ import autopath
|
|||
from py.test import raises
|
||||
from tiramisu.config import *
|
||||
from tiramisu.option import *
|
||||
from tiramisu.error import MandatoryError
|
||||
from tiramisu.error import PropertiesOptionError
|
||||
|
||||
def make_description():
|
||||
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
|
||||
|
@ -50,27 +50,6 @@ def test_set_defaut_value_from_option_object():
|
|||
b = BoolOption("boolean", "", default=False)
|
||||
assert b.getdefault() == False
|
||||
|
||||
def test_mandatory():
|
||||
dummy1 = BoolOption('dummy1', 'doc dummy', properties=('mandatory', ))
|
||||
dummy2 = BoolOption('dummy2', 'doc dummy', properties=('mandatory', ))
|
||||
group = OptionDescription('group', '', [dummy1, dummy2])
|
||||
config = Config(group)
|
||||
setting = config.cfgimpl_get_settings()
|
||||
setting.read_only()
|
||||
# config.setoption('dummy1', True)
|
||||
raises(MandatoryError, 'config.dummy1')
|
||||
setting.read_write()
|
||||
config.dummy1 = True
|
||||
setting.read_only()
|
||||
assert config.dummy1 == True
|
||||
raises(MandatoryError, 'config.dummy2 == None')
|
||||
# raises(MandatoryError, "config.override({'dummy2':None})")
|
||||
setting.read_write()
|
||||
config.set(dummy2=True)
|
||||
config.dummy2 = False
|
||||
setting.read_only()
|
||||
assert config.dummy2 == False
|
||||
|
||||
def test_force_default_on_freeze():
|
||||
"a frozen option wich is forced returns his default"
|
||||
dummy1 = BoolOption('dummy1', 'doc dummy', default=False, properties=('force_default_on_freeze',))
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
# ____________________________________________________________
|
||||
#from inspect import getmembers, ismethod
|
||||
from tiramisu.error import (PropertiesOptionError, ConfigError,
|
||||
AmbigousOptionError, MandatoryError)
|
||||
AmbigousOptionError)
|
||||
from tiramisu.option import OptionDescription, Option, SymLinkOption
|
||||
from tiramisu.setting import groups, Setting, apply_requires
|
||||
from tiramisu.value import Values
|
||||
|
@ -79,13 +79,13 @@ class SubConfig(object):
|
|||
return homeconfig.__setattr__(name, value)
|
||||
child = getattr(self._cfgimpl_descr, name)
|
||||
if type(child) != SymLinkOption:
|
||||
self._validate(name, getattr(self._cfgimpl_descr, name), force_permissive=force_permissive)
|
||||
self._validate(name, getattr(self._cfgimpl_descr, name), value,
|
||||
force_permissive=force_permissive)
|
||||
self.setoption(name, child, value)
|
||||
else:
|
||||
child.setoption(self.cfgimpl_get_context(), value)
|
||||
|
||||
def _validate(self, name, opt_or_descr, force_permissive=False):
|
||||
"validation for the setattr and the getattr"
|
||||
def _validate_descr(self, name, opt_or_descr, force_permissive=False, is_raise=True):
|
||||
if not isinstance(opt_or_descr, Option) and \
|
||||
not isinstance(opt_or_descr, OptionDescription):
|
||||
raise TypeError(_('unexpected object: {0}').format(repr(opt_or_descr)))
|
||||
|
@ -98,6 +98,24 @@ class SubConfig(object):
|
|||
properties = properties - set(self.cfgimpl_get_settings().get_permissive())
|
||||
properties = properties - set(self.cfgimpl_get_settings().get_permissive(opt_or_descr))
|
||||
properties = list(properties)
|
||||
if is_raise:
|
||||
if properties != []:
|
||||
raise PropertiesOptionError(_("trying to access"
|
||||
" to an option named: {0} with properties"
|
||||
" {1}").format(name, str(properties)),
|
||||
properties)
|
||||
else:
|
||||
return properties
|
||||
|
||||
def _validate(self, name, opt_or_descr, value, force_permissive=False,
|
||||
force_properties=None):
|
||||
"validation for the setattr and the getattr"
|
||||
properties = self._validate_descr(name, opt_or_descr,
|
||||
force_permissive=force_permissive,
|
||||
is_raise=False)
|
||||
if self.cfgimpl_get_context().cfgimpl_get_values().is_mandatory_err(
|
||||
opt_or_descr, value, force_properties=force_properties):
|
||||
properties.append('mandatory')
|
||||
if properties != []:
|
||||
raise PropertiesOptionError(_("trying to access"
|
||||
" to an option named: {0} with properties"
|
||||
|
@ -130,8 +148,8 @@ class SubConfig(object):
|
|||
rootconfig = self.cfgimpl_get_context()
|
||||
path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt)
|
||||
return rootconfig._getattr(path, validate=validate)
|
||||
self._validate(name, opt_or_descr, force_permissive=force_permissive)
|
||||
if isinstance(opt_or_descr, OptionDescription):
|
||||
self._validate_descr(name, opt_or_descr, force_permissive=force_permissive)
|
||||
children = self.cfgimpl_get_description()._children
|
||||
if opt_or_descr not in children[1]:
|
||||
raise AttributeError(_("{0} with name {1} object has "
|
||||
|
@ -143,9 +161,12 @@ class SubConfig(object):
|
|||
if name.startswith('_cfgimpl_'):
|
||||
# if it were in __dict__ it would have been found already
|
||||
object.__getattr__(self, name)
|
||||
return self.cfgimpl_get_values()._getitem(opt_or_descr,
|
||||
force_properties=force_properties,
|
||||
value = self.cfgimpl_get_values()._getitem(opt_or_descr,
|
||||
validate=validate)
|
||||
self._validate(name, opt_or_descr, value,
|
||||
force_permissive=force_permissive,
|
||||
force_properties=force_properties)
|
||||
return value
|
||||
|
||||
def setoption(self, name, child, value):
|
||||
"""effectively modifies the value of an Option()
|
||||
|
@ -258,13 +279,12 @@ class SubConfig(object):
|
|||
|
||||
__repr__ = __str__
|
||||
|
||||
def getpaths(self, include_groups=False, allpaths=False, mandatory=False):
|
||||
def getpaths(self, include_groups=False, allpaths=False):
|
||||
"""returns a list of all paths in self, recursively, taking care of
|
||||
the context of properties (hidden/disabled)
|
||||
|
||||
:param include_groups: if true, OptionDescription are included
|
||||
:param allpaths: all the options (event the properties protected ones)
|
||||
:param mandatory: includes the mandatory options
|
||||
:returns: list of all paths
|
||||
"""
|
||||
paths = []
|
||||
|
@ -274,9 +294,6 @@ class SubConfig(object):
|
|||
else:
|
||||
try:
|
||||
getattr(self, path)
|
||||
except MandatoryError:
|
||||
if mandatory:
|
||||
paths.append(path)
|
||||
except PropertiesOptionError:
|
||||
pass
|
||||
else:
|
||||
|
@ -421,12 +438,11 @@ class Config(SubConfig):
|
|||
if len(candidates) == 1:
|
||||
name = '.'.join(candidates[0])
|
||||
homeconfig, name = self.cfgimpl_get_home_by_path(name)
|
||||
try:
|
||||
getattr(homeconfig, name)
|
||||
except MandatoryError:
|
||||
pass
|
||||
except PropertiesOptionError, e:
|
||||
raise e # HiddenOptionError or DisabledOptionError
|
||||
#except MandatoryError:
|
||||
# pass
|
||||
#except PropertiesOptionError, e:
|
||||
# raise e # HiddenOptionError or DisabledOptionError
|
||||
child = getattr(homeconfig._cfgimpl_descr, name)
|
||||
homeconfig.setoption(name, child, value)
|
||||
elif len(candidates) > 1:
|
||||
|
@ -534,7 +550,6 @@ def mandatory_warnings(config):
|
|||
for path in config.cfgimpl_get_description().getpaths(include_groups=True):
|
||||
try:
|
||||
config._getattr(path, force_properties=('mandatory',))
|
||||
except MandatoryError:
|
||||
except PropertiesOptionError, err:
|
||||
if err.proptype == ['mandatory']:
|
||||
yield path
|
||||
except PropertiesOptionError:
|
||||
pass
|
||||
|
|
|
@ -1,3 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# The original `Config` design model is unproudly borrowed from
|
||||
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
|
||||
# the whole pypy projet is under MIT licence
|
||||
# ____________________________________________________________
|
||||
|
||||
#ValueError if function's parameter not correct
|
||||
# or if not logical
|
||||
# or if validation falied
|
||||
|
@ -34,11 +56,6 @@ class RequirementRecursionError(StandardError):
|
|||
pass
|
||||
|
||||
|
||||
class MandatoryError(Exception):
|
||||
"mandatory error"
|
||||
pass
|
||||
|
||||
|
||||
class MultiTypeError(Exception):
|
||||
"""multi must be a list
|
||||
or error with multi length"""
|
||||
|
|
|
@ -78,7 +78,7 @@ class Option(BaseInformation):
|
|||
"""
|
||||
__slots__ = ('_name', '_requires', '_multi', '_validator', '_default_multi',
|
||||
'_default', '_properties', '_callback', '_multitype',
|
||||
'_master_slaves', '_consistencies')
|
||||
'_master_slaves', '_consistencies', '_empty')
|
||||
|
||||
def __init__(self, name, doc, default=None, default_multi=None,
|
||||
requires=None, multi=False, callback=None,
|
||||
|
@ -110,6 +110,7 @@ class Option(BaseInformation):
|
|||
validate_requires_arg(requires, self._name)
|
||||
self._requires = requires
|
||||
self._multi = multi
|
||||
self._empty = ''
|
||||
self._consistencies = None
|
||||
if validator is not None:
|
||||
if type(validator) != FunctionType:
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# ____________________________________________________________
|
||||
from tiramisu.error import MandatoryError, MultiTypeError, ConfigError
|
||||
from tiramisu.error import MultiTypeError, ConfigError
|
||||
from tiramisu.setting import owners, multitypes
|
||||
from tiramisu.autolib import carry_out_calculation
|
||||
from tiramisu.i18n import _
|
||||
|
@ -67,36 +67,25 @@ class Values(object):
|
|||
|
||||
def _is_empty(self, opt, value):
|
||||
"convenience method to know if an option is empty"
|
||||
#FIXME: buggy ?
|
||||
#if value is not None:
|
||||
# return False
|
||||
if (not opt.is_multi() and value is None) or \
|
||||
empty = opt._empty
|
||||
if (not opt.is_multi() and (value is None or value == empty)) or \
|
||||
(opt.is_multi() and (value == [] or
|
||||
None in self._get_value(opt))):
|
||||
None in value or empty in value)):
|
||||
return True
|
||||
if self.is_default_owner(opt) and opt.is_empty_by_default():
|
||||
return True
|
||||
return False
|
||||
|
||||
def _test_mandatory(self, opt, value, force_properties=None):
|
||||
def is_mandatory_err(self, opt, value, force_properties=None):
|
||||
setting = self.context.cfgimpl_get_settings()
|
||||
if force_properties is None:
|
||||
set_mandatory = setting.has_property('mandatory')
|
||||
else:
|
||||
if force_properties is not None:
|
||||
set_mandatory = ('mandatory' in force_properties or
|
||||
setting.has_property('mandatory'))
|
||||
if setting.has_property('mandatory', opt, False) and set_mandatory:
|
||||
if self._is_empty(opt, value) and opt.is_empty_by_default():
|
||||
raise MandatoryError(_("option: {0} is mandatory "
|
||||
"and shall have a value").format(opt._name))
|
||||
#empty value
|
||||
if opt.is_multi():
|
||||
for val in value:
|
||||
if val == '':
|
||||
raise MandatoryError(_("option: {0} is mandatory "
|
||||
"and shall have not empty value").format(opt._name))
|
||||
else:
|
||||
if value == '':
|
||||
raise MandatoryError(_("option: {0} is mandatory "
|
||||
"and shall have not empty value").format(opt._name))
|
||||
set_mandatory)
|
||||
if set_mandatory and setting.has_property('mandatory', opt, False) and \
|
||||
self._is_empty(opt, value):
|
||||
return True
|
||||
return False
|
||||
|
||||
def fill_multi(self, opt, result):
|
||||
"""fills a multi option with default and calculated values
|
||||
|
@ -118,7 +107,7 @@ class Values(object):
|
|||
def __getitem__(self, opt):
|
||||
return self._getitem(opt)
|
||||
|
||||
def _getitem(self, opt, force_properties=None, validate=True):
|
||||
def _getitem(self, opt, validate=True):
|
||||
# options with callbacks
|
||||
value = self._get_value(opt)
|
||||
setting = self.context.cfgimpl_get_settings()
|
||||
|
@ -142,13 +131,12 @@ class Values(object):
|
|||
value = opt.getdefault()
|
||||
if opt.is_multi():
|
||||
value = self.fill_multi(opt, value)
|
||||
self._test_mandatory(opt, value, force_properties)
|
||||
if validate and not opt.validate(value, self.context, setting.has_property('validator')):
|
||||
raise ValueError(_('invalid calculated value returned'
|
||||
' for option {0}: {1}').format(opt._name, value))
|
||||
if self.is_default_owner(opt) and \
|
||||
setting.has_property('force_store_value', opt, False):
|
||||
self.setitem(opt, value)
|
||||
self.setitem(opt, value, validate=validate)
|
||||
return value
|
||||
|
||||
def __setitem__(self, opt, value):
|
||||
|
@ -182,7 +170,6 @@ class Values(object):
|
|||
if type(value) == list:
|
||||
raise MultiTypeError(_("the type of the value {0} which is multi shall "
|
||||
"be Multi and not list").format(str(value)))
|
||||
self._test_mandatory(opt, value)
|
||||
self.values[opt] = (self.context.cfgimpl_get_settings().getowner(), value)
|
||||
|
||||
def __contains__(self, opt):
|
||||
|
|
Loading…
Reference in a new issue