use warnings instead of a new dictionary

This commit is contained in:
Emmanuel Garette 2013-09-26 21:56:06 +02:00
parent f040d3da61
commit f4677b9ef9
4 changed files with 112 additions and 69 deletions

View file

@ -1,9 +1,11 @@
import autopath import autopath
import warnings
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, OptionDescription from tiramisu.option import StrOption, OptionDescription
from tiramisu.setting import groups from tiramisu.setting import groups
from tiramisu.error import ValueWarning
def return_true(value, param=None): def return_true(value, param=None):
@ -76,24 +78,36 @@ def test_validator_warning():
root = OptionDescription('root', '', [opt1, opt2, opt3]) root = OptionDescription('root', '', [opt1, opt2, opt3])
cfg = Config(root) cfg = Config(root)
assert cfg.opt1 == 'val' assert cfg.opt1 == 'val'
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
cfg.opt1 = 'val' cfg.opt1 = 'val'
assert cfg.cfgimpl_get_values().has_warning() is False assert w == []
#
with warnings.catch_warnings(record=True) as w:
cfg.opt2 = 'val' cfg.opt2 = 'val'
assert cfg.cfgimpl_get_values().has_warning() is True assert len(w) == 1
assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error'} assert w[0].message.opt == opt2
assert cfg.cfgimpl_get_values().has_warning() is False assert str(w[0].message) == 'invalid value val for option opt2: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.opt3.append('val') cfg.opt3.append('val')
assert cfg.cfgimpl_get_values().has_warning() is False assert w == []
#
with warnings.catch_warnings(record=True) as w:
cfg.opt3.append('val1') cfg.opt3.append('val1')
assert cfg.cfgimpl_get_values().has_warning() is True assert len(w) == 1
assert cfg.cfgimpl_get_values().get_warnings() == {opt3: 'invalid value val1 for option opt3: error'} assert w[0].message.opt == opt3
assert cfg.cfgimpl_get_values().has_warning() is False assert str(w[0].message) == 'invalid value val1 for option opt3: error'
raises(ValueError, "cfg.opt2 = 1") raises(ValueError, "cfg.opt2 = 1")
#
with warnings.catch_warnings(record=True) as w:
cfg.opt2 = 'val' cfg.opt2 = 'val'
cfg.opt3.append('val') cfg.opt3.append('val')
assert cfg.cfgimpl_get_values().has_warning() is True assert len(w) == 2
assert cfg.cfgimpl_get_values().get_warnings() == {opt2: 'invalid value val for option opt2: error', opt3: 'invalid value val1 for option opt3: error'} assert w[0].message.opt == opt2
assert cfg.cfgimpl_get_values().has_warning() is False assert str(w[0].message) == 'invalid value val for option opt2: error'
assert w[1].message.opt == opt3
assert str(w[1].message) == 'invalid value val1 for option opt3: error'
def test_validator_warning_master_slave(): def test_validator_warning_master_slave():
@ -104,18 +118,38 @@ def test_validator_warning_master_slave():
assert interface1.impl_get_group_type() == groups.master assert interface1.impl_get_group_type() == groups.master
root = OptionDescription('root', '', [interface1]) root = OptionDescription('root', '', [interface1])
cfg = Config(root) cfg = Config(root)
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0.append(None) cfg.ip_admin_eth0.ip_admin_eth0.append(None)
assert cfg.cfgimpl_get_values().has_warning() is False assert w == []
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1']
assert cfg.ip_admin_eth0.netmask_admin_eth0 == ['val1'] assert len(w) == 1
assert cfg.cfgimpl_get_values().has_warning() is True assert w[0].message.opt == netmask_admin_eth0
assert cfg.cfgimpl_get_values().get_warnings() == {netmask_admin_eth0: 'invalid value val1 for option netmask_admin_eth0: error'} assert str(w[0].message) == 'invalid value val1 for option netmask_admin_eth0: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val'] cfg.ip_admin_eth0.ip_admin_eth0 = ['val']
assert cfg.ip_admin_eth0.ip_admin_eth0 == ['val'] assert len(w) == 1
assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1'] cfg.ip_admin_eth0.ip_admin_eth0 = ['val', 'val1', 'val1']
assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'
#
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1'] cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val', 'val1']
assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'
#
warnings.resetwarnings()
with warnings.catch_warnings(record=True) as w:
cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val'] cfg.ip_admin_eth0.ip_admin_eth0 = ['val1', 'val1', 'val']
assert cfg.cfgimpl_get_values().get_warnings() == {ip_admin_eth0: 'invalid value val for option ip_admin_eth0: error'} assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error'

View file

@ -61,3 +61,35 @@ class SlaveError(Exception):
class ConstError(TypeError): class ConstError(TypeError):
"no uniq value in _NameSpace" "no uniq value in _NameSpace"
pass pass
#Warning
class ValueWarning(UserWarning):
"""Option could warn user and not raise ValueError.
Example:
>>> import warnings
>>> from tiramisu.error import ValueWarning
>>> from tiramisu.option import StrOption, OptionDescription
>>> from tiramisu.config import Config
>>> warnings.simplefilter("always", ValueWarning)
>>> def a(val):
... raise ValueError('pouet')
...
>>> s=StrOption('s', '', validator=a, only_warning=True)
>>> o=OptionDescription('o', '', [s])
>>> c=Config(o)
>>> c.s = 'val'
StrOption:0: ValueWarning: invalid value val for option s: pouet
>>> with warnings.catch_warnings(record=True) as w:
... c.s = 'val'
...
>>> w[0].message.opt == s
True
>>> print str(w[0].message)
invalid value val for option s: pouet
"""
def __init__(self, msg, opt):
self.opt = opt
super(ValueWarning, self).__init__(msg)

View file

@ -25,8 +25,9 @@ import sys
from copy import copy, deepcopy from copy import copy, deepcopy
from types import FunctionType from types import FunctionType
from IPy import IP from IPy import IP
import warnings
from tiramisu.error import ConflictError from tiramisu.error import ConflictError, ValueWarning
from tiramisu.setting import groups, multitypes from tiramisu.setting import groups, multitypes
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
@ -474,7 +475,6 @@ class Option(BaseOption):
def do_validation(_value, _index=None): def do_validation(_value, _index=None):
if _value is None: if _value is None:
return return
ret_validation = None
try: try:
# valid with self._validator # valid with self._validator
val_validator(_value) val_validator(_value)
@ -486,12 +486,13 @@ class Option(BaseOption):
msg = _("invalid value {0} for option {1}: {2}").format( msg = _("invalid value {0} for option {1}: {2}").format(
_value, self._name, err) _value, self._name, err)
if self._only_warning: if self._only_warning:
ret_validation = msg warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning,
self.__class__.__name__, 0)
else: else:
raise ValueError(msg) raise ValueError(msg)
# option validation # option validation
self._validate(_value) self._validate(_value)
return ret_validation
# generic calculation # generic calculation
if context is not None: if context is not None:

View file

@ -33,7 +33,7 @@ class Values(object):
but the values are physicaly located here, in `Values`, wich is also but the values are physicaly located here, in `Values`, wich is also
responsible of a caching utility. responsible of a caching utility.
""" """
__slots__ = ('context', '_warning', '_p_', '__weakref__') __slots__ = ('context', '_p_', '__weakref__')
def __init__(self, context, storage): def __init__(self, context, storage):
""" """
@ -45,7 +45,6 @@ class Values(object):
self.context = weakref.ref(context) self.context = weakref.ref(context)
# the storage type is dictionary or sqlite3 # the storage type is dictionary or sqlite3
self._p_ = storage self._p_ = storage
self._warning = {}
def _getdefault(self, opt): def _getdefault(self, opt):
""" """
@ -107,9 +106,9 @@ class Values(object):
path = self._get_opt_path(opt) path = self._get_opt_path(opt)
if self._p_.hasvalue(path): if self._p_.hasvalue(path):
setting = self.context().cfgimpl_get_settings() setting = self.context().cfgimpl_get_settings()
self._setwarning(opt.impl_validate(opt.impl_getdefault(), opt.impl_validate(opt.impl_getdefault(),
self.context(), self.context(),
'validator' in setting), opt) 'validator' in setting)
self.context().cfgimpl_reset_cache() self.context().cfgimpl_reset_cache()
if (opt.impl_is_multi() and if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.master): opt.impl_get_multitype() == multitypes.master):
@ -231,8 +230,7 @@ class Values(object):
else: else:
value = self._getvalue(opt, path, validate) value = self._getvalue(opt, path, validate)
if config_error is None and validate: if config_error is None and validate:
self._setwarning(opt.impl_validate(value, self.context(), opt.impl_validate(value, self.context(), 'validator' in setting)
'validator' in setting), opt)
if config_error is None and self._is_default_owner(path) and \ if config_error is None and self._is_default_owner(path) and \
'force_store_value' in setting[opt]: 'force_store_value' in setting[opt]:
self.setitem(opt, value, path, is_write=False) self.setitem(opt, value, path, is_write=False)
@ -253,9 +251,8 @@ class Values(object):
# is_write is, for example, used with "force_store_value" # is_write is, for example, used with "force_store_value"
# user didn't change value, so not write # user didn't change value, so not write
# valid opt # valid opt
self._setwarning(opt.impl_validate(value, self.context(), opt.impl_validate(value, self.context(),
'validator' in self.context( 'validator' in self.context().cfgimpl_get_settings())
).cfgimpl_get_settings()), opt)
if opt.impl_is_multi() and not isinstance(value, Multi): if opt.impl_is_multi() and not isinstance(value, Multi):
value = Multi(value, self.context, opt, path, setitem=True) value = Multi(value, self.context, opt, path, setitem=True)
self._setvalue(opt, path, value, force_permissive=force_permissive, self._setvalue(opt, path, value, force_permissive=force_permissive,
@ -374,26 +371,6 @@ class Values(object):
def __setstate__(self, states): def __setstate__(self, states):
self._p_ = states['_p_'] self._p_ = states['_p_']
def _setwarning(self, msg, opt):
if msg is not None:
self._warning[opt] = msg
def has_warning(self):
"""If option is "only_warning", validation error is store in
self._warning.
has_warning just indicate that a warning message is store
"""
return self._warning != {}
def get_warnings(self):
"""Get last warnings messages in self._warning.
We can get only one time those messages.
:returns: {opt: msg, opt1: msg1}
"""
ret = self._warning
self._warning = {}
return ret
# ____________________________________________________________ # ____________________________________________________________
# multi types # multi types
@ -564,9 +541,8 @@ class Multi(list):
def _validate(self, value): def _validate(self, value):
if value is not None: if value is not None:
try: try:
self.context().cfgimpl_get_values()._setwarning(
self.opt.impl_validate(value, context=self.context(), self.opt.impl_validate(value, context=self.context(),
force_no_multi=True), self.opt) force_no_multi=True)
except ValueError as err: except ValueError as err:
raise ValueError(_("invalid value {0} " raise ValueError(_("invalid value {0} "
"for option {1}: {2}" "for option {1}: {2}"