mandatory is a true property (no more MandatoryError) + tests

This commit is contained in:
Emmanuel Garette 2013-04-16 22:44:16 +02:00
parent 6097f3af84
commit 656b751995
7 changed files with 293 additions and 92 deletions

View file

@ -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=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'] 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
View 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']

View file

@ -4,7 +4,7 @@ import autopath
from py.test import raises from py.test import raises
from tiramisu.config import * from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
from tiramisu.error import MandatoryError from tiramisu.error import PropertiesOptionError
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') 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) b = BoolOption("boolean", "", default=False)
assert b.getdefault() == 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(): def test_force_default_on_freeze():
"a frozen option wich is forced returns his default" "a frozen option wich is forced returns his default"
dummy1 = BoolOption('dummy1', 'doc dummy', default=False, properties=('force_default_on_freeze',)) dummy1 = BoolOption('dummy1', 'doc dummy', default=False, properties=('force_default_on_freeze',))

View file

@ -22,7 +22,7 @@
# ____________________________________________________________ # ____________________________________________________________
#from inspect import getmembers, ismethod #from inspect import getmembers, ismethod
from tiramisu.error import (PropertiesOptionError, ConfigError, from tiramisu.error import (PropertiesOptionError, ConfigError,
AmbigousOptionError, MandatoryError) AmbigousOptionError)
from tiramisu.option import OptionDescription, Option, SymLinkOption from tiramisu.option import OptionDescription, Option, SymLinkOption
from tiramisu.setting import groups, Setting, apply_requires from tiramisu.setting import groups, Setting, apply_requires
from tiramisu.value import Values from tiramisu.value import Values
@ -79,13 +79,13 @@ class SubConfig(object):
return homeconfig.__setattr__(name, value) return homeconfig.__setattr__(name, value)
child = getattr(self._cfgimpl_descr, name) child = getattr(self._cfgimpl_descr, name)
if type(child) != SymLinkOption: 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) self.setoption(name, child, value)
else: else:
child.setoption(self.cfgimpl_get_context(), value) child.setoption(self.cfgimpl_get_context(), value)
def _validate(self, name, opt_or_descr, force_permissive=False): def _validate_descr(self, name, opt_or_descr, force_permissive=False, is_raise=True):
"validation for the setattr and the getattr"
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)))
@ -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())
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)
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 != []: if properties != []:
raise PropertiesOptionError(_("trying to access" raise PropertiesOptionError(_("trying to access"
" to an option named: {0} with properties" " to an option named: {0} with properties"
@ -130,8 +148,8 @@ class SubConfig(object):
rootconfig = self.cfgimpl_get_context() rootconfig = self.cfgimpl_get_context()
path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt) path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt)
return rootconfig._getattr(path, validate=validate) return rootconfig._getattr(path, validate=validate)
self._validate(name, opt_or_descr, force_permissive=force_permissive)
if isinstance(opt_or_descr, OptionDescription): if isinstance(opt_or_descr, OptionDescription):
self._validate_descr(name, opt_or_descr, force_permissive=force_permissive)
children = self.cfgimpl_get_description()._children children = self.cfgimpl_get_description()._children
if opt_or_descr not in children[1]: if opt_or_descr not in children[1]:
raise AttributeError(_("{0} with name {1} object has " raise AttributeError(_("{0} with name {1} object has "
@ -143,9 +161,12 @@ class SubConfig(object):
if name.startswith('_cfgimpl_'): if name.startswith('_cfgimpl_'):
# if it were in __dict__ it would have been found already # if it were in __dict__ it would have been found already
object.__getattr__(self, name) object.__getattr__(self, name)
return self.cfgimpl_get_values()._getitem(opt_or_descr, value = self.cfgimpl_get_values()._getitem(opt_or_descr,
force_properties=force_properties,
validate=validate) 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): def setoption(self, name, child, value):
"""effectively modifies the value of an Option() """effectively modifies the value of an Option()
@ -258,13 +279,12 @@ class SubConfig(object):
__repr__ = __str__ __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 """returns a list of all paths in self, recursively, taking care of
the context of properties (hidden/disabled) the context of properties (hidden/disabled)
:param include_groups: if true, OptionDescription are included :param include_groups: if true, OptionDescription are included
:param allpaths: all the options (event the properties protected ones) :param allpaths: all the options (event the properties protected ones)
:param mandatory: includes the mandatory options
:returns: list of all paths :returns: list of all paths
""" """
paths = [] paths = []
@ -274,9 +294,6 @@ class SubConfig(object):
else: else:
try: try:
getattr(self, path) getattr(self, path)
except MandatoryError:
if mandatory:
paths.append(path)
except PropertiesOptionError: except PropertiesOptionError:
pass pass
else: else:
@ -421,12 +438,11 @@ class Config(SubConfig):
if len(candidates) == 1: if len(candidates) == 1:
name = '.'.join(candidates[0]) name = '.'.join(candidates[0])
homeconfig, name = self.cfgimpl_get_home_by_path(name) homeconfig, name = self.cfgimpl_get_home_by_path(name)
try:
getattr(homeconfig, name) getattr(homeconfig, name)
except MandatoryError: #except MandatoryError:
pass # pass
except PropertiesOptionError, e: #except PropertiesOptionError, e:
raise e # HiddenOptionError or DisabledOptionError # raise e # HiddenOptionError or DisabledOptionError
child = getattr(homeconfig._cfgimpl_descr, name) child = getattr(homeconfig._cfgimpl_descr, name)
homeconfig.setoption(name, child, value) homeconfig.setoption(name, child, value)
elif len(candidates) > 1: elif len(candidates) > 1:
@ -534,7 +550,6 @@ def mandatory_warnings(config):
for path in config.cfgimpl_get_description().getpaths(include_groups=True): for path in config.cfgimpl_get_description().getpaths(include_groups=True):
try: try:
config._getattr(path, force_properties=('mandatory',)) config._getattr(path, force_properties=('mandatory',))
except MandatoryError: except PropertiesOptionError, err:
if err.proptype == ['mandatory']:
yield path yield path
except PropertiesOptionError:
pass

View file

@ -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 #ValueError if function's parameter not correct
# or if not logical # or if not logical
# or if validation falied # or if validation falied
@ -34,11 +56,6 @@ class RequirementRecursionError(StandardError):
pass pass
class MandatoryError(Exception):
"mandatory error"
pass
class MultiTypeError(Exception): class MultiTypeError(Exception):
"""multi must be a list """multi must be a list
or error with multi length""" or error with multi length"""

View file

@ -78,7 +78,7 @@ class Option(BaseInformation):
""" """
__slots__ = ('_name', '_requires', '_multi', '_validator', '_default_multi', __slots__ = ('_name', '_requires', '_multi', '_validator', '_default_multi',
'_default', '_properties', '_callback', '_multitype', '_default', '_properties', '_callback', '_multitype',
'_master_slaves', '_consistencies') '_master_slaves', '_consistencies', '_empty')
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,
@ -110,6 +110,7 @@ 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._empty = ''
self._consistencies = None self._consistencies = None
if validator is not None: if validator is not None:
if type(validator) != FunctionType: if type(validator) != FunctionType:

View file

@ -17,7 +17,7 @@
# 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 MandatoryError, MultiTypeError, ConfigError from tiramisu.error import MultiTypeError, ConfigError
from tiramisu.setting import owners, multitypes from tiramisu.setting import owners, multitypes
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
from tiramisu.i18n import _ from tiramisu.i18n import _
@ -67,36 +67,25 @@ class Values(object):
def _is_empty(self, opt, value): 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 ? empty = opt._empty
#if value is not None: if (not opt.is_multi() and (value is None or value == empty)) or \
# return False
if (not opt.is_multi() and value is None) or \
(opt.is_multi() and (value == [] 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 True
return False 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() setting = self.context.cfgimpl_get_settings()
if force_properties is None:
set_mandatory = setting.has_property('mandatory') set_mandatory = setting.has_property('mandatory')
else: if force_properties is not None:
set_mandatory = ('mandatory' in force_properties or set_mandatory = ('mandatory' in force_properties or
setting.has_property('mandatory')) set_mandatory)
if setting.has_property('mandatory', opt, False) and set_mandatory: if set_mandatory and setting.has_property('mandatory', opt, False) and \
if self._is_empty(opt, value) and opt.is_empty_by_default(): self._is_empty(opt, value):
raise MandatoryError(_("option: {0} is mandatory " return True
"and shall have a value").format(opt._name)) return False
#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))
def fill_multi(self, opt, result): def fill_multi(self, opt, result):
"""fills a multi option with default and calculated values """fills a multi option with default and calculated values
@ -118,7 +107,7 @@ class Values(object):
def __getitem__(self, opt): def __getitem__(self, opt):
return self._getitem(opt) return self._getitem(opt)
def _getitem(self, opt, force_properties=None, validate=True): def _getitem(self, opt, validate=True):
# 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()
@ -142,13 +131,12 @@ class Values(object):
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)
self._test_mandatory(opt, value, force_properties)
if validate and not opt.validate(value, self.context, setting.has_property('validator')): if validate and not opt.validate(value, self.context, setting.has_property('validator')):
raise ValueError(_('invalid calculated value returned' raise ValueError(_('invalid calculated value returned'
' for option {0}: {1}').format(opt._name, value)) ' for option {0}: {1}').format(opt._name, value))
if self.is_default_owner(opt) and \ if self.is_default_owner(opt) and \
setting.has_property('force_store_value', opt, False): setting.has_property('force_store_value', opt, False):
self.setitem(opt, value) self.setitem(opt, value, validate=validate)
return value return value
def __setitem__(self, opt, value): def __setitem__(self, opt, value):
@ -182,7 +170,6 @@ class Values(object):
if type(value) == list: if type(value) == list:
raise MultiTypeError(_("the type of the value {0} which is multi shall " raise MultiTypeError(_("the type of the value {0} which is multi shall "
"be Multi and not list").format(str(value))) "be Multi and not list").format(str(value)))
self._test_mandatory(opt, value)
self.values[opt] = (self.context.cfgimpl_get_settings().getowner(), value) self.values[opt] = (self.context.cfgimpl_get_settings().getowner(), value)
def __contains__(self, opt): def __contains__(self, opt):