Merge branch 'master' into lgpl

This commit is contained in:
Emmanuel Garette 2014-03-15 10:11:17 +01:00
commit a04a61f1a4
32 changed files with 2711 additions and 1230 deletions

View File

@ -10,8 +10,6 @@ Tiramisu is made of almost three main objects :
- :class:`tiramisu.option.OptionDescription` is the shema, the option's structure - :class:`tiramisu.option.OptionDescription` is the shema, the option's structure
- :class:`tiramisu.config.Config` which is the whole configuration entry point - :class:`tiramisu.config.Config` which is the whole configuration entry point
.. image:: config.png
Accessing the `Option`'s Accessing the `Option`'s
------------------------- -------------------------
@ -47,9 +45,13 @@ object is returned, and if no `Option` has been declared in the
The `Option` objects (in this case the :class:`~tiramisu.option.BoolOption`), The `Option` objects (in this case the :class:`~tiramisu.option.BoolOption`),
are organized into a tree into nested are organized into a tree into nested
:class:`~tiramisu.option.OptionDescription` objects. Every option has a name, :class:`~tiramisu.option.OptionDescription` objects.
as does every option group. The parts of the full name of the option are
separated by dots: e.g. ``cfg.optgroup.optname``. .. image:: config.png
Every option has a name, as does every option group. The parts
of the full name of the option are separated by dots: e.g.
``cfg.optgroup.optname``.
Let's make the protocol of accessing a `Config`'s attribute explicit Let's make the protocol of accessing a `Config`'s attribute explicit
(because explicit is better than implicit): (because explicit is better than implicit):
@ -363,6 +365,10 @@ read/write or read only mode::
>>> print c.od1.var3 >>> print c.od1.var3
value value
Many properties can be defined at the same time on an option::
>>> c.cfgimpl_get_settings().extend(['unknown1', 'unknown2'])
Properties can also be defined on an option group (that is, on an Properties can also be defined on an option group (that is, on an
:term:`option description`) let's hide a group and try to access to it:: :term:`option description`) let's hide a group and try to access to it::

View File

@ -4,7 +4,11 @@ from tiramisu import setting
setting.expires_time = 1 setting.expires_time = 1
from tiramisu.option import IntOption, OptionDescription from tiramisu.option import IntOption, OptionDescription
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.error import ConfigError
from time import sleep, time from time import sleep, time
from py.test import raises
def make_description(): def make_description():
@ -253,3 +257,25 @@ def test_reset_cache_only():
c.cfgimpl_reset_cache(only=('settings',)) c.cfgimpl_reset_cache(only=('settings',))
assert 'u1' in values._p_.get_cached(c) assert 'u1' in values._p_.get_cached(c)
assert 'u1' not in settings._p_.get_cached(c) assert 'u1' not in settings._p_.get_cached(c)
def test_force_cache():
u1 = IntOption('u1', '', multi=True)
u2 = IntOption('u2', '')
u3 = IntOption('u3', '', multi=True)
u4 = IntOption('u4', '', properties=('disabled',))
od = OptionDescription('od1', '', [u1, u2, u3, u4])
c = Config(od)
c.cfgimpl_get_settings().remove('expire')
c.cfgimpl_get_values().force_cache()
assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': ([], None), 'u3': ([], None), 'u2': (None, None), 'u4': (None, None)}
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u4': (set(['disabled']), None), 'u1': (set([]), None), 'u3': (set([]), None), 'u2': (set([]), None)}
c.read_only()
c.cfgimpl_get_values().force_cache()
assert c.cfgimpl_get_values()._p_.get_cached(c) == {'u1': ([], None), 'u3': ([], None), 'u2': (None, None)}
assert c.cfgimpl_get_settings()._p_.get_cached(c) == {'u4': (set(['disabled']), None), 'u1': (set([]), None), 'u3': (set([]), None), 'u2': (set([]), None)}
c.cfgimpl_get_settings().remove('cache')
raises(ConfigError, "c.cfgimpl_get_values().force_cache()")

View File

@ -6,10 +6,11 @@
import autopath import autopath
from py.test import raises from py.test import raises
from tiramisu.config import Config from tiramisu.config import Config, SubConfig
from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \
BoolOption, UnicodeOption, OptionDescription BoolOption, UnicodeOption, OptionDescription
from tiramisu.error import ConflictError from tiramisu.error import ConflictError, ConfigError
import weakref
def make_description(): def make_description():
@ -131,8 +132,10 @@ def test_cfgimpl_get_home_by_path():
config.bool = False config.bool = False
assert config.cfgimpl_get_home_by_path('gc.dummy')[1] == 'dummy' assert config.cfgimpl_get_home_by_path('gc.dummy')[1] == 'dummy'
assert config.cfgimpl_get_home_by_path('dummy')[1] == 'dummy' assert config.cfgimpl_get_home_by_path('dummy')[1] == 'dummy'
#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_not_valid_properties():
raises(TypeError, "stroption = StrOption('str', 'Test string option', default='abc', properties=['mandatory',])")
def test_information_config(): def test_information_config():
@ -142,6 +145,7 @@ def test_information_config():
config.impl_set_information('info', string) config.impl_set_information('info', string)
assert config.impl_get_information('info') == string assert config.impl_get_information('info') == string
raises(ValueError, "config.impl_get_information('noinfo')") raises(ValueError, "config.impl_get_information('noinfo')")
assert config.impl_get_information('noinfo', 'default') == 'default'
def test_config_impl_get_path_by_opt(): def test_config_impl_get_path_by_opt():
@ -149,8 +153,10 @@ def test_config_impl_get_path_by_opt():
config = Config(descr) config = Config(descr)
dummy = config.unwrap_from_path('gc.dummy') dummy = config.unwrap_from_path('gc.dummy')
boo = config.unwrap_from_path('bool') boo = config.unwrap_from_path('bool')
unknown = IntOption('test', '')
assert config.cfgimpl_get_description().impl_get_path_by_opt(boo) == 'bool' assert config.cfgimpl_get_description().impl_get_path_by_opt(boo) == 'bool'
assert config.cfgimpl_get_description().impl_get_path_by_opt(dummy) == 'gc.dummy' assert config.cfgimpl_get_description().impl_get_path_by_opt(dummy) == 'gc.dummy'
raises(AttributeError, "config.cfgimpl_get_description().impl_get_path_by_opt(unknown)")
def test_config_impl_get_opt_by_path(): def test_config_impl_get_opt_by_path():
@ -160,6 +166,7 @@ def test_config_impl_get_opt_by_path():
boo = config.unwrap_from_path('bool') boo = config.unwrap_from_path('bool')
assert config.cfgimpl_get_description().impl_get_opt_by_path('bool') == boo assert config.cfgimpl_get_description().impl_get_opt_by_path('bool') == boo
assert config.cfgimpl_get_description().impl_get_opt_by_path('gc.dummy') == dummy assert config.cfgimpl_get_description().impl_get_opt_by_path('gc.dummy') == dummy
raises(AttributeError, "config.cfgimpl_get_description().impl_get_opt_by_path('gc.unknown')")
def test_information_display(): def test_information_display():
@ -231,8 +238,83 @@ def test_duplicated_option():
#in different OptionDescription #in different OptionDescription
raises(ConflictError, "config = Config(root)") raises(ConflictError, "config = Config(root)")
def test_cannot_assign_value_to_option_description(): def test_cannot_assign_value_to_option_description():
descr = make_description() descr = make_description()
cfg = Config(descr) cfg = Config(descr)
raises(TypeError, "cfg.gc = 3") raises(TypeError, "cfg.gc = 3")
def test_config_multi():
i1 = IntOption('test1', '', multi=True)
i2 = IntOption('test2', '', multi=True, default_multi=1)
i3 = IntOption('test3', '', default=[2], multi=True, default_multi=1)
od = OptionDescription('test', '', [i1, i2, i3])
config = Config(od)
assert config.test1 == []
assert config.test2 == []
config.test2.append()
assert config.test2 == [1]
assert config.test3 == [2]
config.test3.append()
assert config.test3 == [2, 1]
def test_no_validation():
i1 = IntOption('test1', '')
od = OptionDescription('test', '', [i1])
c = Config(od)
setting = c.cfgimpl_get_settings()
c.test1 = 1
raises(ValueError, 'c.test1 = "yes"')
assert c.test1 == 1
setting.remove('validator')
c.test1 = "yes"
assert c.test1 == "yes"
setting.append('validator')
raises(ValueError, 'c.test1')
del(c.test1)
assert c.test1 is None
def test_delete_config_with_subconfig():
test = IntOption('test', '')
multi = IntOption('multi', '', multi=True)
od = OptionDescription('od', '', [test, multi])
odroot = OptionDescription('odroot', '', [od])
c = Config(odroot)
sub = c.od
val = c.cfgimpl_get_values()
setting = c.cfgimpl_get_settings()
val[test]
val[multi]
setting[test]
sub.make_dict()
del(c)
raises(ConfigError, 'val[test]')
raises(ConfigError, 'val[multi]')
raises(ConfigError, 'setting[test]')
raises(ConfigError, 'sub.make_dict()')
def test_config_weakref():
o = OptionDescription('val', '', [])
o2 = OptionDescription('val', '', [o])
c = Config(o2)
SubConfig(o, weakref.ref(c))
raises(ValueError, "SubConfig(o, c)")
s = SubConfig(o, weakref.ref(c))
assert s._cfgimpl_get_context() == c
del(c)
raises(ConfigError, "s._cfgimpl_get_context()")
def test_config_str():
gcdummy = BoolOption('dummy', 'dummy', default=False)
gcdummy1 = BoolOption('dummy1', 'dummy', default=False, properties=('disabled',))
o = OptionDescription('o', '', [gcdummy, gcdummy1])
descr = OptionDescription('tiramisu', '', [o])
cfg = Config(descr)
cfg.read_only()
str(cfg)
str(cfg.o)

View File

@ -4,26 +4,34 @@ from py.test import raises
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \ from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \
BoolOption, OptionDescription BoolOption, FilenameOption, UnicodeOption, SymLinkOption, IPOption, \
PortOption, NetworkOption, NetmaskOption, BroadcastOption, \
DomainnameOption, OptionDescription
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')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
prop = BoolOption('prop', '', properties=('disabled',))
prop2 = BoolOption('prop', '', properties=('hidden',))
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
('std', 'thunk'), 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
booloption2 = BoolOption('bool', 'Test boolean option', default=False)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption2 = FloatOption('float', 'Test float option', default=2.3)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Tests', default=False) wantref_option = BoolOption('wantref', 'Tests', default=False)
wantframework_option = BoolOption('wantframework', 'Test', default=False) wantframework_option = BoolOption('wantframework', 'Test', default=False)
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) gcgroup2 = OptionDescription('gc2', '', [booloption2, prop])
gcgroup = OptionDescription('gc', '', [gcgroup2, gcoption, gcdummy, floatoption, prop2])
descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption, descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
wantref_option, stroption, wantref_option, stroption,
wantframework_option, wantframework_option,
intoption, boolop]) intoption, boolop, floatoption2])
return descr return descr
@ -62,6 +70,17 @@ def test_iter_config():
[('string', 'string'), ('string2', 'string2')] [('string', 'string'), ('string2', 'string2')]
def test_iter_config_property():
"iteration on config object"
s = StrOption("string", "", default="string", properties=('disabled',))
s2 = StrOption("string2", "", default="string2")
descr = OptionDescription("options", "", [s, s2])
config = Config(descr)
config.read_only()
assert [(name, value) for name, value in config] == \
[('string2', 'string2')]
def test_iter_subconfig(): def test_iter_subconfig():
"iteration on config sub object" "iteration on config sub object"
descr = make_description() descr = make_description()
@ -96,24 +115,57 @@ def test_make_dict():
raises(ValueError, 'd2 = config.make_dict(withvalue="3")') raises(ValueError, 'd2 = config.make_dict(withvalue="3")')
def test_make_dict_with_disabled():
descr = OptionDescription("opt", "", [
OptionDescription("s1", "", [
BoolOption("a", "", default=False),
BoolOption("b", "", default=False, properties=('disabled',))]),
IntOption("int", "", default=42)])
config = Config(descr)
config.read_only()
d = config.make_dict()
assert d == {"s1.a": False, "int": 42}
def test_find_in_config(): def test_find_in_config():
"finds option in config" "finds option in config"
descr = make_description() descr = make_description()
conf = Config(descr) conf = Config(descr)
conf.read_only()
assert conf.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')] assert conf.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
assert conf.find(byname='float') == [conf.unwrap_from_path('gc.float'), conf.unwrap_from_path('float')]
assert conf.find_first(byname='bool') == conf.unwrap_from_path('gc.gc2.bool')
assert conf.find_first(byname='bool', byvalue=True) == conf.unwrap_from_path('bool')
assert conf.find_first(byname='dummy') == conf.unwrap_from_path('gc.dummy') assert conf.find_first(byname='dummy') == conf.unwrap_from_path('gc.dummy')
assert conf.find_first(byname='float') == conf.unwrap_from_path('gc.float')
assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_path('gc.name'), conf.unwrap_from_path('objspace')] assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_path('gc.name'), conf.unwrap_from_path('objspace')]
assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_path('gc.name') assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_path('gc.name')
assert conf.find(byvalue='ref') == [conf.unwrap_from_path('gc.name')] assert conf.find(byvalue='ref') == [conf.unwrap_from_path('gc.name')]
assert conf.find_first(byvalue='ref') == conf.unwrap_from_path('gc.name') assert conf.find_first(byvalue='ref') == conf.unwrap_from_path('gc.name')
assert conf.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
conf.read_write()
raises(AttributeError, "assert conf.find(byname='prop')")
assert conf.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')]
#assert conf.find_first(byname='prop') == conf.unwrap_from_path('gc.prop')
# combinaison of filters # combinaison of filters
assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.dummy')] assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_path('gc.dummy') assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_path('gc.dummy')
assert conf.find(byvalue=False, byname='dummy') == [conf.unwrap_from_path('gc.dummy')] assert conf.find(byvalue=False, byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
assert conf.find_first(byvalue=False, byname='dummy') == conf.unwrap_from_path('gc.dummy') assert conf.find_first(byvalue=False, byname='dummy') == conf.unwrap_from_path('gc.dummy')
## byattrs #subconfig
#assert conf.find_first(byattrs= dict(default=2.3)) == conf.unwrap_from_path('gc.float') assert conf.gc.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
#assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy') assert conf.gc.find(byname='float') == [conf.unwrap_from_path('gc.float')]
assert conf.gc.find(byname='bool') == [conf.unwrap_from_path('gc.gc2.bool')]
assert conf.gc.find_first(byname='bool', byvalue=False) == conf.unwrap_from_path('gc.gc2.bool')
raises(AttributeError, "assert conf.gc.find_first(byname='bool', byvalue=True)")
raises(AttributeError, "conf.gc.find(byname='wantref').first()")
assert conf.gc.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')]
conf.read_only()
assert conf.gc.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
# not OptionDescription
raises(AttributeError, "conf.find_first(byname='gc')")
raises(AttributeError, "conf.gc.find_first(byname='gc2')")
raises(ValueError, "conf.find(byname='bool', type_='unknown')")
def test_find_multi(): def test_find_multi():
@ -137,3 +189,81 @@ def test_does_not_find_in_config():
descr = make_description() descr = make_description()
conf = Config(descr) conf = Config(descr)
raises(AttributeError, "conf.find(byname='IDontExist')") raises(AttributeError, "conf.find(byname='IDontExist')")
def test_filename():
a = FilenameOption('a', '')
o = OptionDescription('o', '', [a])
c = Config(o)
c.a = u'/'
c.a = u'/tmp'
c.a = u'/tmp/'
c.a = u'/tmp/text.txt'
c.a = u'tmp'
c.a = u'tmp/'
c.a = u'tmp/text.txt'
raises(ValueError, "c.a = u'/tmp/with space.txt'")
raises(ValueError, "c.a = u'/tmp/with$.txt'")
def test_iter_all():
s = StrOption("string", "", default="string")
s2 = StrOption("string2", "", default="string2")
descr = OptionDescription("options", "", [s, s2])
config = Config(descr)
assert list(config.iter_all()) == [('string', 'string'), ('string2', 'string2')]
for i in config.iter_all():
#test StopIteration
break
def test_iter_all_prop():
s = StrOption("string", "", default="string", properties=('disabled',))
s2 = StrOption("string2", "", default="string2")
descr = OptionDescription("options", "", [s, s2])
config = Config(descr)
config.read_only()
assert list(config.iter_all()) == [('string2', 'string2')]
def test_impl_getpaths():
s = StrOption("string", "", default="string", properties=('disabled',))
s2 = StrOption("string2", "", default="string2")
s3 = StrOption("string3", "", default="string3")
s4 = StrOption("string4", "", default="string4", properties=('hidden',))
od = OptionDescription('od', '', [s3, s4])
descr = OptionDescription("options", "", [s, s2, od])
config = Config(descr)
assert ['string', 'string2', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths()
assert ['string', 'string2', 'od', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths(include_groups=True)
config.read_write()
raises(PropertiesOptionError, "config.od.string4")
assert ['string', 'string2', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths()
assert ['string', 'string2', 'od', 'od.string3', 'od.string4'] == config.cfgimpl_get_description().impl_getpaths(include_groups=True)
def test_invalid_option():
raises(TypeError, "ChoiceOption('a', '', [1, 2])")
raises(TypeError, "ChoiceOption('a', '', 1)")
raises(TypeError, "ChoiceOption('a', '', (1,), open_values='string')")
raises(ValueError, "ChoiceOption('a', '', (1,), 3)")
raises(ValueError, "FloatOption('a', '', 'string')")
raises(ValueError, "UnicodeOption('a', '', 1)")
raises(ValueError, "SymLinkOption('a', 'string')")
raises(ValueError, "IPOption('a', '', 1)")
raises(ValueError, "IPOption('a', '', 'string')")
raises(ValueError, "PortOption('a', '', 'string')")
raises(ValueError, "PortOption('a', '', '11:12:13', allow_range=True)")
raises(ValueError, "PortOption('a', '', 11111111111111111111)")
raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=False, allow_registred=True, allow_private=False)")
raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=True, allow_registred=False, allow_private=True)")
raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=False, allow_registred=False, allow_private=True)")
raises(ValueError, "PortOption('a', '', allow_zero=True, allow_wellknown=False, allow_registred=True, allow_private=True)")
raises(ValueError, "PortOption('a', '', allow_zero=False, allow_wellknown=False, allow_registred=False, allow_private=False)")
raises(ValueError, "NetworkOption('a', '', 'string')")
raises(ValueError, "NetmaskOption('a', '', 'string')")
raises(ValueError, "BroadcastOption('a', '', 'string')")
raises(ValueError, "DomainnameOption('a', '', 'string')")
raises(ValueError, "DomainnameOption('a', '', type_='string')")
raises(ValueError, "DomainnameOption('a', '', allow_ip='string')")
raises(ValueError, "DomainnameOption('a', '', allow_without_dot='string')")

View File

@ -2,23 +2,47 @@ import autopath
from py.test import raises from py.test import raises
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import DomainnameOption, OptionDescription from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDescription
def test_domainname(): def test_domainname():
d = DomainnameOption('d', '') d = DomainnameOption('d', '')
e = DomainnameOption('e', '', "toto.com") f = DomainnameOption('f', '', allow_without_dot=True)
od = OptionDescription('a', '', [d, e]) g = DomainnameOption('g', '', allow_ip=True)
od = OptionDescription('a', '', [d, f, g])
c = Config(od) c = Config(od)
c.read_write() c.read_write()
c.d = 'toto.com' c.d = 'toto.com'
raises(ValueError, "c.d = 'toto'") raises(ValueError, "c.d = 'toto'")
c.d = 'toto3.com' c.d = 'toto3.com'
c.d = 'toto3.3la' raises(ValueError, "c.d = 'toto3.3la'")
raises(ValueError, "c.d = '3toto.com'") #raises(ValueError, "c.d = '3toto.com'")
c.d = 'toto.co3' raises(ValueError, "c.d = 'toto.co3'")
raises(ValueError, "c.d = 'toto_super.com'") raises(ValueError, "c.d = 'toto_super.com'")
c.d = 'toto-.com' c.d = 'toto-.com'
raises(ValueError, "c.d = 'toto..com'")
#
c.f = 'toto.com'
c.f = 'toto'
raises(ValueError, "c.f = 'domainnametoolongthathavemorethanmaximumsizeforatruedomainnameanditsnoteasytogeneratesolongdomainnamewithoutrepeatdomainnametoolongthathavemorethanmaximumsizeforatruedomainnameanditsnoteasytogeneratesolongdomainnamewithoutrepeatbutimnotabletodoitnowiendityeah'")
raises(ValueError, "c.f = 'd'")
#
c.g = 'toto.com'
c.g = '192.168.1.0'
c.g = '192.168.1.29'
def test_special_domain_name():
"""domain name option that starts with a number or not
"""
d = DomainnameOption('d', '')
e = DomainnameOption('e', '', type_='netbios')
od = OptionDescription('a', '', [d,e])
c = Config(od)
c.read_write()
c.d = '1toto.com'
c.d = '123toto.com'
c.e = 'toto'
raises(ValueError, "c.e = '1toto'")
def test_domainname_netbios(): def test_domainname_netbios():
@ -41,3 +65,35 @@ def test_domainname_hostname():
raises(ValueError, "c.d = 'toto.com'") raises(ValueError, "c.d = 'toto.com'")
c.d = 'toto' c.d = 'toto'
c.d = 'domainnametoolong' c.d = 'domainnametoolong'
def test_email():
e = EmailOption('e', '')
od = OptionDescription('a', '', [e])
c = Config(od)
c.read_write()
c.e = 'root@foo.com'
raises(ValueError, "c.e = 'root'")
raises(ValueError, "c.e = 'root@domain'")
raises(ValueError, "c.e = 'root[]@domain'")
def test_url():
u = URLOption('u', '')
od = OptionDescription('a', '', [u])
c = Config(od)
c.read_write()
c.u = 'http://foo.com'
c.u = 'https://foo.com'
c.u = 'https://foo.com/'
raises(ValueError, "c.u = 'ftp://foo.com'")
raises(ValueError, "c.u = 'foo.com'")
raises(ValueError, "c.u = ':/foo.com'")
raises(ValueError, "c.u = 'foo.com/http://'")
c.u = 'https://foo.com/index.html'
c.u = 'https://foo.com/index.html?var=value&var2=val2'
raises(ValueError, "c.u = 'https://foo.com/index\\n.html'")
c.u = 'https://foo.com:8443'
c.u = 'https://foo.com:8443/'
c.u = 'https://foo.com:8443/index.html'
raises(ValueError, "c.u = 'https://foo.com:84438989'")

View File

@ -21,6 +21,11 @@ def test_ip():
c.b = '0.0.0.0' c.b = '0.0.0.0'
raises(ValueError, "c.b = '255.255.255.0'") raises(ValueError, "c.b = '255.255.255.0'")
raises(ValueError, "IPOption('a', 'ip', default='192.000.023.01')")
d = IPOption('a', 'ip', default='192.0.23.1')
od = OptionDescription('od', '', [d])
c = Config(od)
raises(ValueError, "c.a = '192.000.023.01'")
def test_ip_default(): def test_ip_default():
a = IPOption('a', '', '88.88.88.88') a = IPOption('a', '', '88.88.88.88')

View File

@ -2,8 +2,8 @@
import autopath import autopath
#from py.test import raises #from py.test import raises
from tiramisu.config import Config from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.option import BoolOption, OptionDescription from tiramisu.option import BoolOption, IntOption, OptionDescription
import weakref import weakref
@ -109,3 +109,31 @@ def test_deref_optiondescription_config():
assert w() is not None assert w() is not None
del(c) del(c)
assert w() is None assert w() is None
def test_deref_groupconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
meta = GroupConfig([conf1, conf2])
w = weakref.ref(conf1)
del(conf1)
assert w() is not None
del(meta)
assert w() is None
def test_deref_metaconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
meta = MetaConfig([conf1, conf2])
w = weakref.ref(conf1)
del(conf1)
assert w() is not None
del(meta)
assert w() is None

View File

@ -1,7 +1,7 @@
import autopath import autopath
#from py.test import raises #from py.test import raises
from tiramisu.config import Config, mandatory_warnings 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
@ -205,11 +205,11 @@ def test_mandatory_warnings_ro():
except PropertiesOptionError as err: except PropertiesOptionError as err:
proc = err.proptype proc = err.proptype
assert proc == ['mandatory'] assert proc == ['mandatory']
assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3']
config.read_write() config.read_write()
config.str = 'a' config.str = 'a'
config.read_only() config.read_only()
assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3']
def test_mandatory_warnings_rw(): def test_mandatory_warnings_rw():
@ -218,9 +218,9 @@ def test_mandatory_warnings_rw():
config.str = '' config.str = ''
config.read_write() config.read_write()
config.str config.str
assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3']
config.str = 'a' config.str = 'a'
assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3']
def test_mandatory_warnings_disabled(): def test_mandatory_warnings_disabled():
@ -230,9 +230,9 @@ def test_mandatory_warnings_disabled():
setting = config.cfgimpl_get_settings() setting = config.cfgimpl_get_settings()
config.read_write() config.read_write()
config.str config.str
assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3']
setting[descr.str].append('disabled') setting[descr.str].append('disabled')
assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3']
def test_mandatory_warnings_frozen(): def test_mandatory_warnings_frozen():
@ -242,7 +242,7 @@ def test_mandatory_warnings_frozen():
setting = config.cfgimpl_get_settings() setting = config.cfgimpl_get_settings()
config.read_write() config.read_write()
config.str config.str
assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3']
setting[descr.str].append('frozen') setting[descr.str].append('frozen')
config.read_only() config.read_only()
assert list(mandatory_warnings(config)) == ['str', 'str1', 'unicode2', 'str3'] assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str', 'str1', 'unicode2', 'str3']

View File

@ -1,172 +1,203 @@
#import autopath import autopath
#from py.test import raises from py.test import raises
#from tiramisu.setting import owners from tiramisu.setting import owners
#from tiramisu.config import Config, MetaConfig from tiramisu.config import Config, GroupConfig, MetaConfig
#from tiramisu.option import IntOption, OptionDescription from tiramisu.option import IntOption, OptionDescription
#from tiramisu.error import ConfigError from tiramisu.error import ConfigError, PropertiesOptionError
#owners.addowner('meta') owners.addowner('meta')
#def make_description(): def make_description():
# i1 = IntOption('i1', '') i1 = IntOption('i1', '')
# i2 = IntOption('i2', '', default=1) i2 = IntOption('i2', '', default=1)
# i3 = IntOption('i3', '') i3 = IntOption('i3', '')
# i4 = IntOption('i4', '', default=2) i4 = IntOption('i4', '', default=2)
# od1 = OptionDescription('od1', '', [i1, i2, i3, i4]) i5 = IntOption('i5', '', default=[2], multi=True)
# od2 = OptionDescription('od2', '', [od1]) i6 = IntOption('i6', '', properties=('disabled',))
# conf1 = Config(od2) od1 = OptionDescription('od1', '', [i1, i2, i3, i4, i5, i6])
# conf2 = Config(od2) od2 = OptionDescription('od2', '', [od1])
# meta = MetaConfig([conf1, conf2]) conf1 = Config(od2)
# meta.cfgimpl_get_settings().setowner(owners.meta) conf2 = Config(od2)
# return meta conf1.read_write()
conf2.read_write()
meta = MetaConfig([conf1, conf2])
meta.cfgimpl_get_settings().setowner(owners.meta)
return meta
##FIXME ne pas mettre 2 meta dans une config #FIXME ne pas mettre 2 meta dans une config
##FIXME ne pas mettre 2 OD differents dans un meta #FIXME ne pas mettre 2 OD differents dans un meta
#def test_none(): #FIXME serialization
# meta = make_description() def test_none():
# conf1, conf2 = meta._impl_children meta = make_description()
# assert conf1.od1.i3 is conf2.od1.i3 is None conf1, conf2 = meta.cfgimpl_get_children()
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default assert conf1.od1.i3 is conf2.od1.i3 is None
# meta.od1.i3 = 3 assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default
# assert conf1.od1.i3 == conf2.od1.i3 == 3 meta.od1.i3 = 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta assert conf1.od1.i3 == conf2.od1.i3 == 3
# meta.od1.i3 = 3 assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
# conf1.od1.i3 = 2 meta.od1.i3 = 3
# assert conf1.od1.i3 == 2 conf1.od1.i3 = 2
# assert conf2.od1.i3 == 3 assert conf1.od1.i3 == 2
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user assert conf2.od1.i3 == 3
# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
# meta.od1.i3 = 4 assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
# assert conf1.od1.i3 == 2 meta.od1.i3 = 4
# assert conf2.od1.i3 == 4 assert conf1.od1.i3 == 2
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user assert conf2.od1.i3 == 4
# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
# del(meta.od1.i3) assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
# assert conf1.od1.i3 == 2 del(meta.od1.i3)
# assert conf2.od1.i3 is None assert conf1.od1.i3 == 2
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user assert conf2.od1.i3 is None
# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
# del(conf1.od1.i3) assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default
# assert conf1.od1.i3 is conf2.od1.i3 is None del(conf1.od1.i3)
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default assert conf1.od1.i3 is conf2.od1.i3 is None
assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default
#def test_default(): def test_default():
# meta = make_description() meta = make_description()
# conf1, conf2 = meta._impl_children conf1, conf2 = meta.cfgimpl_get_children()
# assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.od1.i2 == conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# meta.od1.i2 = 3 meta.od1.i2 = 3
# assert conf1.od1.i2 == conf2.od1.i2 == 3 assert conf1.od1.i2 == conf2.od1.i2 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# meta.od1.i2 = 3 meta.od1.i2 = 3
# conf1.od1.i2 = 2 conf1.od1.i2 = 2
# assert conf1.od1.i2 == 2 assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 3 assert conf2.od1.i2 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# meta.od1.i2 = 4 meta.od1.i2 = 4
# assert conf1.od1.i2 == 2 assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 4 assert conf2.od1.i2 == 4
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# del(meta.od1.i2) del(meta.od1.i2)
# assert conf1.od1.i2 == 2 assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 1 assert conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# del(conf1.od1.i2) del(conf1.od1.i2)
# assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.od1.i2 == conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
#def test_contexts(): def test_contexts():
# meta = make_description() meta = make_description()
# conf1, conf2 = meta._impl_children conf1, conf2 = meta.cfgimpl_get_children()
# assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.od1.i2 == conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# meta.set_contexts('od1.i2', 6) meta.setattrs('od1.i2', 6)
# assert meta.od1.i2 == 1 assert meta.od1.i2 == 1
# assert conf1.od1.i2 == conf2.od1.i2 == 6 assert conf1.od1.i2 == conf2.od1.i2 == 6
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.user assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.user
#def test_find(): def test_find():
# meta = make_description() meta = make_description()
# i2 = meta.unwrap_from_path('od1.i2') i2 = meta.unwrap_from_path('od1.i2')
# assert [i2] == meta.find(byname='i2') assert [i2] == meta.find(byname='i2')
# assert i2 == meta.find_first(byname='i2') assert i2 == meta.find_first(byname='i2')
# assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None, 'od1.i2': 1} assert meta.make_dict() == {'od1.i4': 2, 'od1.i1': None, 'od1.i3': None,
'od1.i2': 1, 'od1.i5': [2], 'od1.i6': None}
#def test_meta_meta(): def test_meta_meta():
# meta1 = make_description() meta1 = make_description()
# meta2 = MetaConfig([meta1]) meta2 = MetaConfig([meta1])
# meta2.cfgimpl_get_settings().setowner(owners.meta) meta2.cfgimpl_get_settings().setowner(owners.meta)
# conf1, conf2 = meta1._impl_children conf1, conf2 = meta1.cfgimpl_get_children()
# assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.od1.i2 == conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# meta2.od1.i2 = 3 meta2.od1.i2 = 3
# assert conf1.od1.i2 == conf2.od1.i2 == 3 assert conf1.od1.i2 == conf2.od1.i2 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# meta2.od1.i2 = 3 meta2.od1.i2 = 3
# conf1.od1.i2 = 2 conf1.od1.i2 = 2
# assert conf1.od1.i2 == 2 assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 3 assert conf2.od1.i2 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# meta2.od1.i2 = 4 meta2.od1.i2 = 4
# assert conf1.od1.i2 == 2 assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 4 assert conf2.od1.i2 == 4
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# del(meta2.od1.i2) del(meta2.od1.i2)
# assert conf1.od1.i2 == 2 assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 1 assert conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# del(conf1.od1.i2) del(conf1.od1.i2)
# assert conf1.od1.i2 == conf2.od1.i2 == 1 assert conf1.od1.i2 == conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# meta1.od1.i2 = 6 meta1.od1.i2 = 6
# assert conf1.od1.i2 == conf2.od1.i2 == 6 assert conf1.od1.i2 == conf2.od1.i2 == 6
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
#def test_meta_meta_set(): def test_meta_meta_set():
# meta1 = make_description() meta1 = make_description()
# meta2 = MetaConfig([meta1]) meta2 = MetaConfig([meta1])
# meta2.cfgimpl_get_settings().setowner(owners.meta) meta2.cfgimpl_get_settings().setowner(owners.meta)
# conf1, conf2 = meta1._impl_children conf1, conf2 = meta1.cfgimpl_get_children()
# meta2.set_contexts('od1.i1', 7) meta2.setattrs('od1.i1', 7)
# assert conf1.od1.i1 == conf2.od1.i1 == 7 #PropertiesOptionError
# assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user meta2.setattrs('od1.i6', 7)
# assert [conf1, conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) assert conf1.od1.i1 == conf2.od1.i1 == 7
# conf1.od1.i1 = 8 assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user
# assert [conf2] == meta2.find_first_contexts(byname='i1', byvalue=7) assert [conf1, conf2] == meta2.find_firsts(byname='i1', byvalue=7)
# assert [conf1] == meta2.find_first_contexts(byname='i1', byvalue=8) conf1.od1.i1 = 8
# raises(AttributeError, "meta2.find_first_contexts(byname='i1', byvalue=10)") assert [conf1, conf2] == meta2.find_firsts(byname='i1')
assert [conf2] == meta2.find_firsts(byname='i1', byvalue=7)
assert [conf1] == meta2.find_firsts(byname='i1', byvalue=8)
assert [conf1, conf2] == meta2.find_firsts(byname='i5', byvalue=2)
raises(AttributeError, "meta2.find_firsts(byname='i1', byvalue=10)")
raises(AttributeError, "meta2.find_firsts(byname='not', byvalue=10)")
raises(AttributeError, "meta2.find_firsts(byname='i6')")
#def test_not_meta(): def test_not_meta():
# i1 = IntOption('i1', '') i1 = IntOption('i1', '')
# od1 = OptionDescription('od1', '', [i1]) od1 = OptionDescription('od1', '', [i1])
# od2 = OptionDescription('od2', '', [od1]) od2 = OptionDescription('od2', '', [od1])
# conf1 = Config(od2) conf1 = Config(od2)
# conf2 = Config(od2) conf2 = Config(od2)
# meta = MetaConfig([conf1, conf2], False) raises(ValueError, "GroupConfig(conf1)")
# raises(ConfigError, 'meta.od1.i1') meta = GroupConfig([conf1, conf2])
# conf1, conf2 = meta._impl_children raises(ConfigError, 'meta.od1.i1')
# meta.set_contexts('od1.i1', 7) conf1, conf2 = meta.cfgimpl_get_children()
# assert conf1.od1.i1 == conf2.od1.i1 == 7 meta.setattrs('od1.i1', 7)
# assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user assert conf1.od1.i1 == conf2.od1.i1 == 7
assert conf1.getowner(conf1.unwrap_from_path('od1.i1')) is conf2.getowner(conf2.unwrap_from_path('od1.i1')) is owners.user
#def test_meta_path(): def test_meta_path():
# meta = make_description() meta = make_description()
# assert meta._impl_path is None assert meta._impl_path is None
# assert meta.od1._impl_path == 'od1' assert meta.od1._impl_path == 'od1'
def test_meta_unconsistent():
i1 = IntOption('i1', '')
i2 = IntOption('i2', '', default=1)
i3 = IntOption('i3', '')
i4 = IntOption('i4', '', default=2)
od1 = OptionDescription('od1', '', [i1, i2, i3, i4])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
conf3 = Config(od2)
conf4 = Config(od1)
meta = MetaConfig([conf1, conf2])
meta.cfgimpl_get_settings().setowner(owners.meta)
raises(TypeError, 'MetaConfig("string")')
raises(ValueError, "MetaConfig([conf1, conf3])")
raises(ValueError, "MetaConfig([conf3, conf4])")

21
test/test_multi.py Normal file
View File

@ -0,0 +1,21 @@
# coding: utf-8
import autopath
from tiramisu.value import Multi
from tiramisu.option import IntOption, OptionDescription
from tiramisu.config import Config
from tiramisu.error import ConfigError
import weakref
from py.test import raises
def test_multi():
i = IntOption('int', '', multi=True)
o = OptionDescription('od', '', [i])
c = Config(o)
multi = Multi([1,2,3], weakref.ref(c), i, 'int')
raises(ValueError, "Multi([1,2,3], c, i, 'int')")
raises(ValueError, "Multi(multi, weakref.ref(c), i, 'int')")
assert c is multi._getcontext()
del(c)
raises(ConfigError, "multi._getcontext()")

View File

@ -2,8 +2,13 @@
and to compare them and to compare them
""" """
import autopath import autopath
from py.test import raises
from tiramisu.option import BoolOption, IntOption from tiramisu.option import IntOption, OptionDescription
def a_func():
return None
#def test_option_comparison(): #def test_option_comparison():
@ -36,3 +41,60 @@ from tiramisu.option import BoolOption, IntOption
# assert dummy1 != dummy5 # assert dummy1 != dummy5
# assert dummy1 == dummy6 # assert dummy1 == dummy6
# assert dummy1 != dummy7 # assert dummy1 != dummy7
def test_option_valid_name():
IntOption('test', '')
raises(ValueError, 'IntOption(1, "")')
raises(ValueError, 'IntOption("impl_test", "")')
raises(ValueError, 'IntOption("_test", "")')
raises(ValueError, 'IntOption("unwrap_from_path", "")')
def test_option_with_callback():
#no default value with callback
raises(ValueError, "IntOption('test', '', default=1, callback=a_func)")
def test_option_get_information():
description = "it's ok"
string = 'some informations'
i = IntOption('test', description)
i.impl_set_information('info', string)
assert i.impl_get_information('info') == string
raises(ValueError, "i.impl_get_information('noinfo')")
assert i.impl_get_information('noinfo', 'default') == 'default'
assert i.impl_get_information('doc') == description
assert i.impl_getdoc() == description
def test_optiondescription_get_information():
description = "it's ok"
string = 'some informations'
o = OptionDescription('test', description, [])
o.impl_set_information('info', string)
assert o.impl_get_information('info') == string
raises(ValueError, "o.impl_get_information('noinfo')")
assert o.impl_get_information('noinfo', 'default') == 'default'
assert o.impl_get_information('doc') == description
assert o.impl_getdoc() == description
def test_option_multi():
IntOption('test', '', multi=True)
IntOption('test', '', multi=True, default_multi=1)
IntOption('test', '', default=[1], multi=True, default_multi=1)
#add default_multi to not multi's option
raises(ValueError, "IntOption('test', '', default_multi=1)")
#unvalid default_multi
raises(ValueError, "IntOption('test', '', multi=True, default_multi='yes')")
#not default_multi with callback
raises(ValueError, "IntOption('test', '', multi=True, default_multi=1, callback=a_func)")
def test_option_is_multi_by_default():
assert IntOption('test', '').impl_is_empty_by_default() is True
assert IntOption('test', '', 1).impl_is_empty_by_default() is False
assert IntOption('test', '', multi=True).impl_is_empty_by_default() is True
assert IntOption('test', '', [1], multi=True).impl_is_empty_by_default() is False
assert IntOption('test', '', multi=True, default_multi=1).impl_is_empty_by_default() is True

View File

@ -21,7 +21,13 @@ def return_list(value=None):
def return_list2(*args): def return_list2(*args):
return list(args) l = []
for arg in args:
if isinstance(arg, list):
l.extend(arg)
else:
l.append(arg)
return l
def return_value(value=None): def return_value(value=None):
@ -34,6 +40,10 @@ def return_value2(*args, **kwargs):
return value return value
def return_calc(i, j, k):
return i + j + k
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -93,83 +103,6 @@ def test_identical_paths():
raises(ConflictError, "make_description_duplicates()") raises(ConflictError, "make_description_duplicates()")
def make_description2():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False)
floatoption = FloatOption('float', 'Test float option', default=2.3)
objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0)
stroption = StrOption('str', 'Test string option', default="abc")
# first multi
boolop = BoolOption('boolop', 'Test boolean option op', default=True)
boolop.enable_multi()
wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=({'option': boolop, 'expected': True, 'action': 'hidden'},))
# second multi
wantframework_option = BoolOption('wantframework', 'Test requires',
default=False,
requires=({'option': boolop, 'expected': True, 'action': 'hidden'},))
wantframework_option.enable_multi()
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
wantref_option, stroption,
wantframework_option,
intoption, boolop])
return descr
# FIXME: il faudra tester les validations sur les multis
#def test_multi_constraints():
# "a multi in a constraint has to have the same length"
# descr = make_description2()
# cfg = Config(descr)
# cfg.boolop = [True, True, False]
# cfg.wantframework = [False, False, True]
#
#def test_multi_raise():
# "a multi in a constraint has to have the same length"
# # FIXME fusionner les deux tests, MAIS PROBLEME :
# # il ne devrait pas etre necessaire de refaire une config
# # si la valeur est modifiee une deuxieme fois ->
# #raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
# # ExceptionFailure: 'DID NOT RAISE'
# descr = make_description2()
# cfg = Config(descr)
# cfg.boolop = [True]
# raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
# ____________________________________________________________
# adding dynamically new options description schema
#def test_newoption_add_in_descr():
# descr = make_description()
# newoption = BoolOption('newoption', 'dummy twoo', default=False)
# descr.add_child(newoption)
# config = Config(descr)
# assert config.newoption == False
#def test_newoption_add_in_subdescr():
# descr = make_description()
# newoption = BoolOption('newoption', 'dummy twoo', default=False)
# descr.gc.add_child(newoption)
# config = Config(descr)
# config.bool = False
# assert config.gc.newoption == False
#def test_newoption_add_in_config():
# descr = make_description()
# config = Config(descr)
# config.bool = False
# newoption = BoolOption('newoption', 'dummy twoo', default=False)
# descr.add_child(newoption)
# config.cfgimpl_update()
# assert config.newoption == False
# ____________________________________________________________
def make_description_requires(): def make_description_requires():
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -310,6 +243,19 @@ def test_callback():
assert cfg.val1 == 'val' assert cfg.val1 == 'val'
def test_callback_params_without_callback():
raises(ValueError, "StrOption('val2', '', callback_params={'': ('yes',)})")
def test_callback_invalid():
raises(ValueError, 'val1 = StrOption("val1", "", callback="string")')
raises(ValueError, 'val1 = StrOption("val1", "", callback=return_val, callback_params="string")')
val1 = StrOption('val1', "", 'val')
raises(ValueError, "StrOption('val2', '', callback=return_value, callback_params={'': 'string'})")
raises(ValueError, "StrOption('val4', '', callback=return_value, callback_params={'value': (('string', False),)})")
raises(ValueError, "StrOption('val4', '', callback=return_value, callback_params={'value': ((val1, 'string'),)})")
def test_callback_value(): def test_callback_value():
val1 = StrOption('val1', "", 'val') val1 = StrOption('val1', "", 'val')
val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)}) val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)})
@ -421,12 +367,15 @@ def test_callback_multi_value():
cfg.val1.append('new-val2') cfg.val1.append('new-val2')
assert cfg.val1 == ['new-val', 'new-val2'] assert cfg.val1 == ['new-val', 'new-val2']
assert cfg.val2 == ['new-val', 'new-val2'] assert cfg.val2 == ['new-val', 'new-val2']
assert cfg.val4 == ['new-val', 'yes', 'new-val2', 'yes'] assert cfg.val4 == ['new-val', 'new-val2', 'yes']
del(cfg.val1) del(cfg.val1)
assert cfg.val1 == ['val'] assert cfg.val1 == ['val']
assert cfg.val2 == ['val'] assert cfg.val2 == ['val']
assert cfg.val3 == ['yes'] assert cfg.val3 == ['yes']
assert cfg.val4 == ['val', 'yes'] assert cfg.val4 == ['val', 'yes']
cfg.val2.append('new')
assert cfg.val1 == ['val']
assert cfg.val2 == ['val', 'new']
def test_callback_multi_list(): def test_callback_multi_list():
@ -443,6 +392,14 @@ def test_callback_multi_list():
assert cfg.val1 == ['val', 'val'] assert cfg.val1 == ['val', 'val']
def test_callback_multi_list_extend():
val1 = StrOption('val1', "", callback=return_list2, callback_params={'': (['1', '2', '3'], ['4', '5'])}, multi=True)
maconfig = OptionDescription('rootconfig', '', [val1])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val1 == ['1', '2', '3', '4', '5']
def test_callback_master_and_slaves_master(): def test_callback_master_and_slaves_master():
val1 = StrOption('val1', "", multi=True, callback=return_val) val1 = StrOption('val1', "", multi=True, callback=return_val)
val2 = StrOption('val2', "", multi=True) val2 = StrOption('val2', "", multi=True)
@ -452,7 +409,7 @@ def test_callback_master_and_slaves_master():
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert cfg.val1.val1 == ['val'] assert cfg.val1.val1 == ['val']
cfg.val1.val1.append(None) cfg.val1.val1.append()
assert cfg.val1.val1 == ['val', 'val'] assert cfg.val1.val1 == ['val', 'val']
assert cfg.val1.val2 == [None, None] assert cfg.val1.val2 == [None, None]
@ -467,7 +424,7 @@ def test_callback_master_and_slaves_master_list():
cfg.read_write() cfg.read_write()
assert cfg.val1.val1 == ['val', 'val'] assert cfg.val1.val1 == ['val', 'val']
assert cfg.val1.val2 == [None, None] assert cfg.val1.val2 == [None, None]
cfg.val1.val1.append(None) cfg.val1.val1.append()
assert cfg.val1.val1 == ['val', 'val', None] assert cfg.val1.val1 == ['val', 'val', None]
assert cfg.val1.val2 == [None, None, None] assert cfg.val1.val2 == [None, None, None]
del(cfg.val1.val1) del(cfg.val1.val1)
@ -512,6 +469,66 @@ def test_callback_master_and_slaves_slave():
assert cfg.val1.val2 == ['val2', 'val2', 'val'] assert cfg.val1.val2 == ['val2', 'val2', 'val']
def test_callback_master_and_slaves_slave_cal():
val3 = StrOption('val3', "", multi=True)
val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)})
val2 = StrOption('val2', "", multi=True, callback=return_val)
interface1 = OptionDescription('val1', '', [val1, val2])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1, val3])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val3 == []
assert cfg.val1.val1 == []
assert cfg.val1.val2 == []
cfg.val1.val1 = ['val1']
cfg.val3 = ['val1']
assert cfg.val1.val1 == ['val1']
assert cfg.val1.val2 == ['val']
assert cfg.val1.val1 == ['val1']
assert cfg.val1.val2 == ['val']
del(cfg.val1.val1)
cfg.val1.val2 = ['val']
cfg.val3 = ['val1', 'val2']
assert cfg.val1.val2 == ['val', 'val']
assert cfg.val1.val1 == ['val1', 'val2']
cfg.val1.val2 = ['val1', 'val2']
cfg.val3.pop(1)
# cannot remove slave's value because master is calculated
# so raise
raises(SlaveError, "cfg.val1.val1")
raises(SlaveError, "cfg.val1.val2")
cfg.val3 = ['val1', 'val2', 'val3']
assert cfg.val1.val2 == ['val1', 'val2', 'val']
def test_callback_master_and_slaves_slave_cal2():
val3 = StrOption('val3', "", ['val', 'val'], multi=True)
val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val3, False),)})
val2 = StrOption('val2', "", ['val2', 'val2'], multi=True)
interface1 = OptionDescription('val1', '', [val1, val2])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1, val3])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val3 == ['val', 'val']
assert cfg.val1.val1 == ['val', 'val']
assert cfg.val1.val2 == ['val2', 'val2']
cfg.val3.pop(1)
# # cannot remove slave's value because master is calculated
# # so raise
raises(SlaveError, "cfg.val1.val1")
raises(SlaveError, "cfg.val1.val2")
cfg.val3 = ['val', 'val']
assert cfg.val3 == ['val', 'val']
assert cfg.val1.val1 == ['val', 'val']
assert cfg.val1.val2 == ['val2', 'val2']
raises(SlaveError, "cfg.val1.val1 = ['val']")
assert cfg.val3 == ['val', 'val']
assert cfg.val1.val1 == ['val', 'val']
assert cfg.val1.val2 == ['val2', 'val2']
def test_callback_master_and_slaves_slave_list(): def test_callback_master_and_slaves_slave_list():
val1 = StrOption('val1', "", multi=True) val1 = StrOption('val1', "", multi=True)
val2 = StrOption('val2', "", multi=True, callback=return_list) val2 = StrOption('val2', "", multi=True, callback=return_list)
@ -535,7 +552,8 @@ def test_callback_master_and_slaves_value():
val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)}) val3 = StrOption('val3', "", multi=True, callback=return_value, callback_params={'': ('yes',)})
val4 = StrOption('val4', '', multi=True, default=['val10', 'val11']) val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)}) val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val4, False),)})
interface1 = OptionDescription('val1', '', [val1, val2, val3, val5]) val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val5, False),)})
interface1 = OptionDescription('val1', '', [val1, val2, val3, val5, val6])
interface1.impl_set_group_type(groups.master) interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1, val4]) maconfig = OptionDescription('rootconfig', '', [interface1, val4])
cfg = Config(maconfig) cfg = Config(maconfig)
@ -544,30 +562,35 @@ def test_callback_master_and_slaves_value():
assert cfg.val1.val2 == [] assert cfg.val1.val2 == []
assert cfg.val1.val3 == [] assert cfg.val1.val3 == []
assert cfg.val1.val5 == [] assert cfg.val1.val5 == []
assert cfg.val1.val6 == []
# #
cfg.val1.val1 = ['val1'] cfg.val1.val1 = ['val1']
assert cfg.val1.val1 == ['val1'] assert cfg.val1.val1 == ['val1']
assert cfg.val1.val2 == ['val1'] assert cfg.val1.val2 == ['val1']
assert cfg.val1.val3 == ['yes'] assert cfg.val1.val3 == ['yes']
assert cfg.val1.val5 == ['val10'] assert cfg.val1.val5 == ['val10']
assert cfg.val1.val6 == ['val10']
# #
cfg.val1.val1.append('val2') cfg.val1.val1.append('val2')
assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val1 == ['val1', 'val2']
assert cfg.val1.val2 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2']
assert cfg.val1.val3 == ['yes', 'yes'] assert cfg.val1.val3 == ['yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11'] assert cfg.val1.val5 == ['val10', 'val11']
assert cfg.val1.val6 == ['val10', 'val11']
# #
cfg.val1.val1 = ['val1', 'val2', 'val3'] cfg.val1.val1 = ['val1', 'val2', 'val3']
assert cfg.val1.val1 == ['val1', 'val2', 'val3'] assert cfg.val1.val1 == ['val1', 'val2', 'val3']
assert cfg.val1.val2 == ['val1', 'val2', 'val3'] assert cfg.val1.val2 == ['val1', 'val2', 'val3']
assert cfg.val1.val3 == ['yes', 'yes', 'yes'] assert cfg.val1.val3 == ['yes', 'yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11', None] assert cfg.val1.val5 == ['val10', 'val11', None]
assert cfg.val1.val6 == ['val10', 'val11', None]
# #
cfg.val1.val1.pop(2) cfg.val1.val1.pop(2)
assert cfg.val1.val1 == ['val1', 'val2'] assert cfg.val1.val1 == ['val1', 'val2']
assert cfg.val1.val2 == ['val1', 'val2'] assert cfg.val1.val2 == ['val1', 'val2']
assert cfg.val1.val3 == ['yes', 'yes'] assert cfg.val1.val3 == ['yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11'] assert cfg.val1.val5 == ['val10', 'val11']
assert cfg.val1.val6 == ['val10', 'val11']
# #
cfg.val1.val2 = ['val2', 'val2'] cfg.val1.val2 = ['val2', 'val2']
cfg.val1.val3 = ['val2', 'val2'] cfg.val1.val3 = ['val2', 'val2']
@ -575,11 +598,13 @@ def test_callback_master_and_slaves_value():
assert cfg.val1.val2 == ['val2', 'val2'] assert cfg.val1.val2 == ['val2', 'val2']
assert cfg.val1.val3 == ['val2', 'val2'] assert cfg.val1.val3 == ['val2', 'val2']
assert cfg.val1.val5 == ['val2', 'val2'] assert cfg.val1.val5 == ['val2', 'val2']
assert cfg.val1.val6 == ['val2', 'val2']
# #
cfg.val1.val1.append('val3') cfg.val1.val1.append('val3')
assert cfg.val1.val2 == ['val2', 'val2', 'val3'] assert cfg.val1.val2 == ['val2', 'val2', 'val3']
assert cfg.val1.val3 == ['val2', 'val2', 'yes'] assert cfg.val1.val3 == ['val2', 'val2', 'yes']
assert cfg.val1.val5 == ['val2', 'val2', None] assert cfg.val1.val5 == ['val2', 'val2', None]
assert cfg.val1.val6 == ['val2', 'val2', None]
cfg.cfgimpl_get_settings().remove('cache') cfg.cfgimpl_get_settings().remove('cache')
cfg.val4 = ['val10', 'val11', 'val12'] cfg.val4 = ['val10', 'val11', 'val12']
#if value is already set, not updated ! #if value is already set, not updated !
@ -587,6 +612,69 @@ def test_callback_master_and_slaves_value():
cfg.val1.val1.append('val3') cfg.val1.val1.append('val3')
cfg.val1.val1 = ['val1', 'val2', 'val3'] cfg.val1.val1 = ['val1', 'val2', 'val3']
assert cfg.val1.val5 == ['val2', 'val2', 'val12'] assert cfg.val1.val5 == ['val2', 'val2', 'val12']
assert cfg.val1.val6 == ['val2', 'val2', 'val12']
def test_callback_master():
val2 = StrOption('val2', "", multi=True, callback=return_value)
val1 = StrOption('val1', "", multi=True, callback=return_value, callback_params={'': ((val2, False),)})
interface1 = OptionDescription('val1', '', [val1, val2])
raises(ValueError, "interface1.impl_set_group_type(groups.master)")
def test_callback_master_and_other_master_slave():
val1 = StrOption('val1', "", multi=True)
val2 = StrOption('val2', "", multi=True)
val3 = StrOption('val3', "", multi=True)
val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
val5 = StrOption('val5', "", multi=True, callback=return_value, callback_params={'': ((val1, False),)})
val6 = StrOption('val6', "", multi=True, callback=return_value, callback_params={'': ((val2, False),)})
interface1 = OptionDescription('val1', '', [val1, val2, val3])
interface1.impl_set_group_type(groups.master)
interface2 = OptionDescription('val4', '', [val4, val5, val6])
interface2.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1, interface2])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val4.val4 == ['val10', 'val11']
assert cfg.val4.val5 == [None, None]
assert cfg.val4.val6 == [None, None]
cfg.val1.val1 = ['yes']
assert cfg.val4.val4 == ['val10', 'val11']
assert cfg.val4.val5 == ['yes', None]
assert cfg.val4.val6 == [None, None]
cfg.val1.val2 = ['no']
assert cfg.val4.val4 == ['val10', 'val11']
assert cfg.val4.val5 == ['yes', None]
assert cfg.val4.val6 == ['no', None]
cfg.val1.val1 = ['yes', 'yes', 'yes']
cfg.val1.val2 = ['no', 'no', 'no']
assert cfg.val4.val4 == ['val10', 'val11']
assert cfg.val4.val5 == ['yes', 'yes']
assert cfg.val4.val6 == ['no', 'no']
def test_callback_different_type():
val = IntOption('val', "", default=2)
val_ = IntOption('val_', "", default=3)
val1 = IntOption('val1', "", multi=True)
val2 = IntOption('val2', "", multi=True, callback=return_calc, callback_params={'': ((val, False), (val1, False)), 'k': ((val_, False),)})
interface1 = OptionDescription('val1', '', [val1, val2])
interface1.impl_set_group_type(groups.master)
maconfig = OptionDescription('rootconfig', '', [interface1, val, val_])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val1.val1 == []
assert cfg.val1.val2 == []
cfg.val1.val1 = [1]
assert cfg.val1.val1 == [1]
assert cfg.val1.val2 == [6]
cfg.val1.val1 = [1, 3]
assert cfg.val1.val1 == [1, 3]
assert cfg.val1.val2 == [6, 8]
cfg.val1.val1 = [1, 3, 5]
assert cfg.val1.val1 == [1, 3, 5]
assert cfg.val1.val2 == [6, 8, 10]
def test_callback_hidden(): def test_callback_hidden():
@ -653,7 +741,7 @@ def test_callback_multi_list_params():
maconfig = OptionDescription('rootconfig', '', [val1, oval2]) maconfig = OptionDescription('rootconfig', '', [val1, oval2])
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert cfg.val2.val2 == ['val', 'val', 'val', 'val'] assert cfg.val2.val2 == ['val', 'val']
def test_callback_multi_list_params_key(): def test_callback_multi_list_params_key():
@ -663,31 +751,4 @@ def test_callback_multi_list_params_key():
maconfig = OptionDescription('rootconfig', '', [val1, oval2]) maconfig = OptionDescription('rootconfig', '', [val1, oval2])
cfg = Config(maconfig) cfg = Config(maconfig)
cfg.read_write() cfg.read_write()
assert cfg.val2.val2 == ['val', 'val', 'val', 'val'] assert cfg.val2.val2 == ['val', 'val']
def test_callback_multi_multi():
val1 = StrOption('val1', "", multi=True, default=['val1', 'val2', 'val3'])
val2 = StrOption('val2', "", multi=True, default=['val11', 'val12'])
val3 = StrOption('val3', "", default='val4')
val4 = StrOption('val4', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val2, False))})
val5 = StrOption('val5', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val3, False))})
val6 = StrOption('val6', "", multi=True, default=['val21', 'val22', 'val23'])
val7 = StrOption('val7', "", multi=True, callback=return_list2, callback_params={'': ((val1, False), (val6, False))})
raises(ValueError, "StrOption('val8', '', multi=True, callback=return_list2, callback_params={'value': ((val1, False), (val6, False))})")
maconfig = OptionDescription('rootconfig', '', [val1, val2, val3, val4, val5, val6, val7])
cfg = Config(maconfig)
cfg.read_write()
raises(ConfigError, "cfg.val4")
assert cfg.val5 == ['val1', 'val4', 'val2', 'val4', 'val3', 'val4']
assert cfg.val7 == ['val1', 'val21', 'val2', 'val22', 'val3', 'val23']
def test_multi_with_no_value():
#First option return [] (so without value)
val1 = StrOption('val1', "", ['val'], multi=True)
val2 = StrOption('val2', "", multi=True)
val3 = StrOption('val3', '', multi=True, callback=return_value, callback_params={'': ((val2, False),), 'value': ((val1, False),)})
od = OptionDescription('od', '', [val1, val2, val3])
c = Config(od)
raises(ConfigError, "c.val3")

View File

@ -5,7 +5,32 @@ from tiramisu.setting import owners, groups
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\ from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
BroadcastOption, SymLinkOption, OptionDescription BroadcastOption, SymLinkOption, OptionDescription
from tiramisu.error import ConfigError from tiramisu.error import ConfigError, ValueWarning
import warnings
def test_consistency():
a = IntOption('a', '')
b = IntOption('b', '')
od = OptionDescription('od', '', [a, b])
a.impl_add_consistency('not_equal', b)
#consistency to itself
raises(ConfigError, "a.impl_add_consistency('not_equal', a)")
#consistency with string
raises(ConfigError, "a.impl_add_consistency('not_equal', 'a')")
def test_consistency_warnings_only():
a = IntOption('a', '')
b = IntOption('b', '')
od = OptionDescription('od', '', [a, b])
a.impl_add_consistency('not_equal', b, warnings_only=True)
c = Config(od)
c.a = 1
warnings.simplefilter("always", ValueWarning)
with warnings.catch_warnings(record=True) as w:
c.b = 1
assert w != []
def test_consistency_not_equal(): def test_consistency_not_equal():
@ -158,6 +183,41 @@ def test_consistency_network_netmask():
raises(ValueError, "c.a = '192.168.1.1'") raises(ValueError, "c.a = '192.168.1.1'")
def test_consistency_ip_in_network():
a = NetworkOption('a', '')
b = NetmaskOption('b', '')
c = IPOption('c', '')
od = OptionDescription('od', '', [a, b, c])
c.impl_add_consistency('in_network', a, b)
cfg = Config(od)
cfg.a = '192.168.1.0'
cfg.b = '255.255.255.0'
cfg.c = '192.168.1.1'
raises(ValueError, "cfg.c = '192.168.2.1'")
def test_consistency_ip_in_network_len_error():
a = NetworkOption('a', '')
b = NetmaskOption('b', '')
c = IPOption('c', '')
od = OptionDescription('od', '', [a, b, c])
c.impl_add_consistency('in_network', a)
cfg = Config(od)
raises(ConfigError, "cfg.a = '192.168.2.0'")
def test_consistency_ip_netmask_network_error():
a = IPOption('a', '')
b = NetworkOption('b', '')
c = NetmaskOption('c', '')
od = OptionDescription('od', '', [a, b, c])
c.impl_add_consistency('ip_netmask', a, b)
c = Config(od)
c.a = '192.168.1.1'
c.b = '192.168.1.0'
raises(ConfigError, "c.c = '255.255.255.0'")
def test_consistency_ip_netmask_error_multi(): def test_consistency_ip_netmask_error_multi():
a = IPOption('a', '', multi=True) a = IPOption('a', '', multi=True)
b = NetmaskOption('b', '') b = NetmaskOption('b', '')
@ -206,6 +266,8 @@ def test_consistency_ip_netmask_multi_master():
c.b = ['255.255.255.255'] c.b = ['255.255.255.255']
c.b = ['255.255.255.0'] c.b = ['255.255.255.0']
raises(ValueError, "c.a = ['192.168.1.0']") raises(ValueError, "c.a = ['192.168.1.0']")
c.a = ['192.168.1.128']
raises(ValueError, "c.b = ['255.255.255.128']")
c.a = ['192.168.1.2', '192.168.1.3'] c.a = ['192.168.1.2', '192.168.1.3']
@ -249,6 +311,20 @@ def test_consistency_broadcast():
c.c[1] = '192.168.2.255' c.c[1] = '192.168.2.255'
def test_consistency_broadcast_error():
a = NetworkOption('a', '', multi=True)
b = NetmaskOption('b', '', multi=True)
c = BroadcastOption('c', '', multi=True)
od = OptionDescription('a', '', [a, b, c])
od.impl_set_group_type(groups.master)
b.impl_add_consistency('network_netmask', a)
c.impl_add_consistency('broadcast', a)
c = Config(od)
c.a = ['192.168.1.0']
c.b = ['255.255.255.0']
raises(ConfigError, "c.c = ['192.168.1.255']")
def test_consistency_broadcast_default(): def test_consistency_broadcast_default():
a = NetworkOption('a', '', '192.168.1.0') a = NetworkOption('a', '', '192.168.1.0')
b = NetmaskOption('b', '', '255.255.255.128') b = NetmaskOption('b', '', '255.255.255.128')
@ -272,3 +348,28 @@ def test_consistency_not_all():
c.a = ['192.168.1.0'] c.a = ['192.168.1.0']
c.b = ['255.255.255.0'] c.b = ['255.255.255.0']
c.c = ['192.168.1.255'] c.c = ['192.168.1.255']
def test_consistency_permissive():
a = IntOption('a', '', 1)
b = IntOption('b', '', 2, properties=('hidden',))
od = OptionDescription('od', '', [a, b])
a.impl_add_consistency('not_equal', b)
c = Config(od)
c.cfgimpl_get_settings().setpermissive(('hidden',))
c.read_write()
c.a = 1
def return_val(*args, **kwargs):
return '192.168.1.1'
def test_consistency_with_callback():
a = NetworkOption('a', '', default='192.168.1.0')
b = NetmaskOption('b', '', default='255.255.255.0')
c = IPOption('c', '', callback=return_val, callback_params={'': ((a, False),)})
od = OptionDescription('od', '', [a, b, c])
c.impl_add_consistency('in_network', a, b)
cfg = Config(od)
cfg.c

View File

@ -5,7 +5,7 @@ from tiramisu.setting import owners
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
StrOption, OptionDescription StrOption, OptionDescription
from tiramisu.error import ConfigError from tiramisu.error import ConfigError, ConstError
def make_description(): def make_description():
@ -36,6 +36,7 @@ def test_default_owner():
cfg = Config(descr) cfg = Config(descr)
assert cfg.dummy is False assert cfg.dummy is False
assert cfg.getowner(gcdummy) == 'default' assert cfg.getowner(gcdummy) == 'default'
raises(TypeError, "cfg.getowner('gcdummy')")
cfg.dummy = True cfg.dummy = True
assert cfg.getowner(gcdummy) == owners.user assert cfg.getowner(gcdummy) == owners.user
@ -52,6 +53,17 @@ def test_addowner():
assert cfg.getowner(gcdummy) == owners.gen_config assert cfg.getowner(gcdummy) == owners.gen_config
def test_addowner_multiple_time():
owners.addowner("testowner")
raises(ConstError, 'owners.addowner("testowner")')
def test_delete_owner():
owners.addowner('deleted')
raises(ConstError, 'del(owners.deleted)')
raises(ValueError, 'del(owners.deleted2)')
def test_owner_is_not_a_string(): def test_owner_is_not_a_string():
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
descr = OptionDescription('tiramisu', '', [gcdummy]) descr = OptionDescription('tiramisu', '', [gcdummy])

View File

@ -310,6 +310,15 @@ def test_access_by_get_whith_hide():
raises(AttributeError, "c.find(byname='b1')") raises(AttributeError, "c.find(byname='b1')")
def test_extend_config_properties():
descr = make_description()
cfg = Config(descr)
setting = cfg.cfgimpl_get_settings()
assert str(setting) == str(['cache', 'expire', 'validator'])
setting.extend(['test', 'test2'])
assert str(setting) == str(['test', 'cache', 'test2', 'expire', 'validator'])
def test_append_properties(): def test_append_properties():
descr = make_description() descr = make_description()
cfg = Config(descr) cfg = Config(descr)
@ -367,3 +376,14 @@ def test_reset_multiple():
setting[option].append('test') setting[option].append('test')
setting.reset(all_properties=True) setting.reset(all_properties=True)
setting.reset(all_properties=True) setting.reset(all_properties=True)
def test_properties_cached():
b1 = BoolOption("b1", "", properties=('test',))
descr = OptionDescription("opt", "", [OptionDescription("sub", "", [b1])])
c = Config(descr)
c.read_write()
setting = c.cfgimpl_get_settings()
option = c.cfgimpl_get_description().sub.b1
c._setattr('sub.b1', True, force_permissive=True)
assert str(setting[b1]) == "['test']"

View File

@ -75,6 +75,16 @@ def test_group_is_hidden():
prop = err.proptype prop = err.proptype
assert 'hidden' in prop assert 'hidden' in prop
def test_extend_properties():
descr = make_description()
config = Config(descr)
setting = config.cfgimpl_get_settings()
config.read_write()
gc = config.unwrap_from_path('gc')
config.unwrap_from_path('gc.dummy')
setting[gc].extend(['hidden', 'user_defined_property'])
assert 'hidden' in setting[gc]
assert 'user_defined_property' in setting[gc]
def test_group_is_hidden_multi(): def test_group_is_hidden_multi():
descr = make_description() descr = make_description()

View File

@ -0,0 +1,25 @@
"configuration objects global API"
import autopath
from py.test import raises
from tiramisu.config import Config
from tiramisu.option import UsernameOption
def test_username():
UsernameOption('a', '', 'string')
UsernameOption('a', '', '_string')
UsernameOption('a', '', 's_tring')
UsernameOption('a', '', 'string_')
UsernameOption('a', '', 'string$')
UsernameOption('a', '', '_string$')
raises(ValueError, "UsernameOption('a', '', 'strin$g')")
UsernameOption('a', '', 's-tring')
raises(ValueError, "UsernameOption('a', '', '-string')")
UsernameOption('a', '', 's9tring')
raises(ValueError, "UsernameOption('a', '', '9string')")
raises(ValueError, "UsernameOption('a', '', '')")
UsernameOption('a', '', 's')
UsernameOption('a', '', 's2345678901234567890123456789012')
raises(ValueError, "UsernameOption('a', '', 's23456789012345678901234567890123')")
UsernameOption('a', '', 's234567890123456789012345678901$')
raises(ValueError, "UsernameOption('a', '', 's2345678901234567890123456789012$')")

View File

@ -6,6 +6,7 @@ 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 from tiramisu.error import ValueWarning
from tiramisu.i18n import _
def return_true(value, param=None): def return_true(value, param=None):
@ -87,7 +88,7 @@ def test_validator_warning():
cfg.opt2 = 'val' cfg.opt2 = 'val'
assert len(w) == 1 assert len(w) == 1
assert w[0].message.opt == opt2 assert w[0].message.opt == opt2
assert str(w[0].message) == 'invalid value val for option opt2: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('opt2', 'error')
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
cfg.opt3.append('val') cfg.opt3.append('val')
@ -97,7 +98,7 @@ def test_validator_warning():
cfg.opt3.append('val1') cfg.opt3.append('val1')
assert len(w) == 1 assert len(w) == 1
assert w[0].message.opt == opt3 assert w[0].message.opt == opt3
assert str(w[0].message) == 'invalid value val1 for option opt3: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('opt3', 'error')
raises(ValueError, "cfg.opt2 = 1") raises(ValueError, "cfg.opt2 = 1")
# #
with warnings.catch_warnings(record=True) as w: with warnings.catch_warnings(record=True) as w:
@ -105,9 +106,9 @@ def test_validator_warning():
cfg.opt3.append('val') cfg.opt3.append('val')
assert len(w) == 2 assert len(w) == 2
assert w[0].message.opt == opt2 assert w[0].message.opt == opt2
assert str(w[0].message) == 'invalid value val for option opt2: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('opt2', 'error')
assert w[1].message.opt == opt3 assert w[1].message.opt == opt3
assert str(w[1].message) == 'invalid value val1 for option opt3: error' assert str(w[1].message) == _("warning on the value of the option {0}: {1}").format('opt3', 'error')
def test_validator_warning_master_slave(): def test_validator_warning_master_slave():
@ -127,29 +128,29 @@ def test_validator_warning_master_slave():
cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1'] cfg.ip_admin_eth0.netmask_admin_eth0 = ['val1']
assert len(w) == 1 assert len(w) == 1
assert w[0].message.opt == netmask_admin_eth0 assert w[0].message.opt == netmask_admin_eth0
assert str(w[0].message) == 'invalid value val1 for option netmask_admin_eth0: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('netmask_admin_eth0', 'error')
# #
with warnings.catch_warnings(record=True) as w: 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 len(w) == 1 assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0 assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('ip_admin_eth0', 'error')
# #
with warnings.catch_warnings(record=True) as w: 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 len(w) == 1 assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0 assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('ip_admin_eth0', 'error')
# #
with warnings.catch_warnings(record=True) as w: 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 len(w) == 1 assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0 assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('ip_admin_eth0', 'error')
# #
warnings.resetwarnings() warnings.resetwarnings()
with warnings.catch_warnings(record=True) as w: 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 len(w) == 1 assert len(w) == 1
assert w[0].message.opt == ip_admin_eth0 assert w[0].message.opt == ip_admin_eth0
assert str(w[0].message) == 'invalid value val for option ip_admin_eth0: error' assert str(w[0].message) == _("warning on the value of the option {0}: {1}").format('ip_admin_eth0', 'error')

View File

@ -94,6 +94,19 @@ def test_iter_on_groups():
result = list(config.creole.iter_groups(group_type=groups.family)) result = list(config.creole.iter_groups(group_type=groups.family))
group_names = [res[0] for res in result] group_names = [res[0] for res in result]
assert group_names == ['general', 'interface1'] assert group_names == ['general', 'interface1']
for i in config.creole.iter_groups(group_type=groups.family):
#test StopIteration
break
def test_iter_on_groups_props():
descr = make_description()
config = Config(descr)
config.read_write()
config.cfgimpl_get_settings()[descr.creole.interface1].append('disabled')
result = list(config.creole.iter_groups(group_type=groups.family))
group_names = [res[0] for res in result]
assert group_names == ['general']
def test_iter_on_empty_group(): def test_iter_on_empty_group():

View File

@ -9,7 +9,8 @@ from tiramisu.error import PropertiesOptionError
def make_description(): def make_description():
u1 = IntOption('u1', '', properties=('frozen', 'mandatory', 'disabled', )) u1 = IntOption('u1', '', properties=('frozen', 'mandatory', 'disabled', ))
return OptionDescription('od1', '', [u1]) u2 = IntOption('u2', '', properties=('frozen', 'mandatory', 'disabled', ))
return OptionDescription('od1', '', [u1, u2])
def test_permissive(): def test_permissive():
@ -91,3 +92,108 @@ def test_invalid_permissive():
setting = config.cfgimpl_get_settings() setting = config.cfgimpl_get_settings()
config.read_write() config.read_write()
raises(TypeError, "setting.setpermissive(['frozen', 'disabled',])") raises(TypeError, "setting.setpermissive(['frozen', 'disabled',])")
def test_permissive_option():
descr = make_description()
u1 = descr.u1
config = Config(descr)
setting = config.cfgimpl_get_settings()
config.read_write()
props = []
try:
config.u1
except PropertiesOptionError as err:
props = err.proptype
assert props == ['disabled']
props = []
try:
config.u2
except PropertiesOptionError as err:
props = err.proptype
assert props == ['disabled']
setting.setpermissive(('disabled',), u1)
props = []
try:
config.u1
except PropertiesOptionError as err:
props = err.proptype
assert props == []
props = []
try:
config.u2
except PropertiesOptionError as err:
props = err.proptype
assert props == ['disabled']
setting.append('permissive')
config.u1
props = []
try:
config.u2
except PropertiesOptionError as err:
props = err.proptype
assert props == ['disabled']
setting.remove('permissive')
props = []
try:
config.u1
except PropertiesOptionError as err:
props = err.proptype
assert props == []
props = []
try:
config.u2
except PropertiesOptionError as err:
props = err.proptype
assert props == ['disabled']
def test_permissive_option_mandatory():
descr = make_description()
u1 = descr.u1
config = Config(descr)
setting = config.cfgimpl_get_settings()
config.read_only()
props = []
try:
config.u1
except PropertiesOptionError as err:
props = err.proptype
assert set(props) == set(['disabled', 'mandatory'])
setting.setpermissive(('mandatory', 'disabled',), u1)
setting.append('permissive')
config.u1
setting.remove('permissive')
try:
config.u1
except PropertiesOptionError as err:
props = err.proptype
assert set(props) == set(['disabled', 'mandatory'])
def test_permissive_option_frozen():
descr = make_description()
config = Config(descr)
u1 = descr.u1
setting = config.cfgimpl_get_settings()
config.read_write()
setting.setpermissive(('frozen', 'disabled'), u1)
config.u1 = 1
assert config.u1 == 1
setting.append('permissive')
assert config.u1 == 1
setting.remove('permissive')
assert config.u1 == 1
def test_invalid_option_permissive():
descr = make_description()
u1 = descr.u1
config = Config(descr)
setting = config.cfgimpl_get_settings()
config.read_write()
raises(TypeError, "setting.setpermissive(['frozen', 'disabled',], u1)")

View File

@ -24,6 +24,42 @@ def test_requires():
except PropertiesOptionError as err: except PropertiesOptionError as err:
props = err.proptype props = err.proptype
assert props == ['disabled'] assert props == ['disabled']
c.activate_service = True
c.ip_address_service
def test_requires_with_requires():
a = BoolOption('activate_service', '', True)
b = IPOption('ip_address_service', '',
requires=[{'option': a, 'expected': False, 'action': 'disabled'}])
od = OptionDescription('service', '', [a, b])
c = Config(od)
c.read_write()
c.cfgimpl_get_settings()[b].append('test')
c.ip_address_service
c.activate_service = False
props = []
try:
c.ip_address_service
except PropertiesOptionError as err:
props = err.proptype
assert props == ['disabled']
c.activate_service = True
c.ip_address_service
def test_requires_invalid():
a = BoolOption('activate_service', '', True)
raises(ValueError, "IPOption('ip_address_service', '', requires='string')")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'unknown': True}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'action': 'disabled'}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'expected': False, 'action': 'disabled'}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'inverse': 'string'}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'transitive': 'string'}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': False, 'action': 'disabled', 'same_action': 'string'}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': 'string', 'expected': False, 'action': 'disabled'}])")
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': 'string', 'action': 'disabled'}])")
def test_requires_same_action(): def test_requires_same_action():
@ -504,6 +540,11 @@ def test_requires_requirement_append():
c.cfgimpl_get_settings()[b].append("test") c.cfgimpl_get_settings()[b].append("test")
def test_requires_different_inverse():
a = BoolOption('activate_service', '', True)
raises(ValueError, "IPOption('ip_address_service', '', requires=[{'option': a, 'expected': True, 'action': 'disabled', 'inverse': True}, {'option': a, 'expected': True, 'action': 'disabled', 'inverse': False}])")
def test_requires_recursive_path(): def test_requires_recursive_path():
a = BoolOption('activate_service', '', True) a = BoolOption('activate_service', '', True)
b = IPOption('ip_address_service', '', b = IPOption('ip_address_service', '',

View File

@ -3,9 +3,10 @@ import autopath
from py.test import raises from py.test import raises
from tiramisu.config import Config, SubConfig from tiramisu.config import Config, SubConfig
from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption, \ from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption,\
StrOption, SymLinkOption, UnicodeOption, IPOption, OptionDescription, \ StrOption, SymLinkOption, UnicodeOption, IPOption, OptionDescription, \
PortOption, NetworkOption, NetmaskOption, DomainnameOption PortOption, NetworkOption, NetmaskOption, DomainnameOption, EmailOption, \
URLOption, FilenameOption
def test_slots_option(): def test_slots_option():
@ -35,6 +36,12 @@ def test_slots_option():
raises(AttributeError, "c.x = 1") raises(AttributeError, "c.x = 1")
c = DomainnameOption('a', '') c = DomainnameOption('a', '')
raises(AttributeError, "c.x = 1") raises(AttributeError, "c.x = 1")
c = EmailOption('a', '')
raises(AttributeError, "c.x = 1")
c = URLOption('a', '')
raises(AttributeError, "c.x = 1")
c = FilenameOption('a', '')
raises(AttributeError, "c.x = 1")
def test_slots_option_readonly(): def test_slots_option_readonly():
@ -49,7 +56,10 @@ def test_slots_option_readonly():
j = NetworkOption('j', '') j = NetworkOption('j', '')
k = NetmaskOption('k', '') k = NetmaskOption('k', '')
l = DomainnameOption('l', '') l = DomainnameOption('l', '')
m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l]) o = EmailOption('o', '')
p = URLOption('p', '')
q = FilenameOption('q', '')
m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l, o, p, q])
a._requires = 'a' a._requires = 'a'
b._requires = 'b' b._requires = 'b'
c._requires = 'c' c._requires = 'c'
@ -62,6 +72,9 @@ def test_slots_option_readonly():
k._requires = 'k' k._requires = 'k'
l._requires = 'l' l._requires = 'l'
m._requires = 'm' m._requires = 'm'
o._requires = 'o'
p._requires = 'p'
q._requires = 'q'
Config(m) Config(m)
raises(AttributeError, "a._requires = 'a'") raises(AttributeError, "a._requires = 'a'")
raises(AttributeError, "b._requires = 'b'") raises(AttributeError, "b._requires = 'b'")
@ -75,6 +88,9 @@ def test_slots_option_readonly():
raises(AttributeError, "k._requires = 'k'") raises(AttributeError, "k._requires = 'k'")
raises(AttributeError, "l._requires = 'l'") raises(AttributeError, "l._requires = 'l'")
raises(AttributeError, "m._requires = 'm'") raises(AttributeError, "m._requires = 'm'")
raises(AttributeError, "o._requires = 'o'")
raises(AttributeError, "p._requires = 'p'")
raises(AttributeError, "q._requires = 'q'")
def test_slots_option_readonly_name(): def test_slots_option_readonly_name():
@ -90,7 +106,10 @@ def test_slots_option_readonly_name():
j = NetworkOption('j', '') j = NetworkOption('j', '')
k = NetmaskOption('k', '') k = NetmaskOption('k', '')
l = DomainnameOption('l', '') l = DomainnameOption('l', '')
m = OptionDescription('m', '', [a, b, c, d, e, f, g, h, i, j, k, l]) o = DomainnameOption('o', '')
p = DomainnameOption('p', '')
q = DomainnameOption('q', '')
m = OptionDescription('m', '', [a, b, c, d, e, f, g, h, i, j, k, l, o, p, q])
raises(AttributeError, "a._name = 'a'") raises(AttributeError, "a._name = 'a'")
raises(AttributeError, "b._name = 'b'") raises(AttributeError, "b._name = 'b'")
raises(AttributeError, "c._name = 'c'") raises(AttributeError, "c._name = 'c'")
@ -104,6 +123,9 @@ def test_slots_option_readonly_name():
raises(AttributeError, "k._name = 'k'") raises(AttributeError, "k._name = 'k'")
raises(AttributeError, "l._name = 'l'") raises(AttributeError, "l._name = 'l'")
raises(AttributeError, "m._name = 'm'") raises(AttributeError, "m._name = 'm'")
raises(AttributeError, "o._name = 'o'")
raises(AttributeError, "p._name = 'p'")
raises(AttributeError, "q._name = 'q'")
def test_slots_description(): def test_slots_description():

View File

@ -1,10 +1,13 @@
import autopath
from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \ from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \
OptionDescription IntOption, OptionDescription
from tiramisu.config import Config from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.setting import owners from tiramisu.setting import owners
from tiramisu.storage import delete_session from tiramisu.storage import delete_session
from tiramisu.error import ConfigError from tiramisu.error import ConfigError
from pickle import dumps, loads from pickle import dumps, loads
from py.test import raises
def return_value(value=None): def return_value(value=None):
@ -90,10 +93,48 @@ def _diff_opt(opt1, opt2):
assert val1 == val2 assert val1 == val2
def _diff_conf(cfg1, cfg2):
attr1 = set(_get_slots(cfg1))
attr2 = set(_get_slots(cfg2))
diff1 = attr1 - attr2
diff2 = attr2 - attr1
if diff1 != set():
raise Exception('more attribute in cfg1 {0}'.format(list(diff1)))
if diff2 != set():
raise Exception('more attribute in cfg2 {0}'.format(list(diff2)))
for attr in attr1:
if attr in ('_impl_context', '__weakref__'):
continue
err1 = False
err2 = False
val1 = None
val2 = None
try:
val1 = getattr(cfg1, attr)
except:
err1 = True
try:
val2 = getattr(cfg2, attr)
except:
err2 = True
assert err1 == err2
if val1 is None:
assert val1 == val2
elif attr == '_impl_values':
assert cfg1.cfgimpl_get_values().get_modified_values() == cfg2.cfgimpl_get_values().get_modified_values()
elif attr == '_impl_settings':
assert cfg1.cfgimpl_get_settings().get_modified_properties() == cfg2.cfgimpl_get_settings().get_modified_properties()
assert cfg1.cfgimpl_get_settings().get_modified_permissives() == cfg2.cfgimpl_get_settings().get_modified_permissives()
elif attr == '_impl_descr':
_diff_opt(cfg1.cfgimpl_get_description(), cfg2.cfgimpl_get_description())
else:
assert val1 == val2
def test_diff_opt(): def test_diff_opt():
b = BoolOption('b', '') b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}]) u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
#u.impl_add_consistency('not_equal', b)
s = SymLinkOption('s', u) s = SymLinkOption('s', u)
o = OptionDescription('o', '', [b, u, s]) o = OptionDescription('o', '', [b, u, s])
o1 = OptionDescription('o1', '', [o]) o1 = OptionDescription('o1', '', [o])
@ -107,6 +148,11 @@ def test_diff_opt():
_diff_opt(o1.o.s, q.o.s) _diff_opt(o1.o.s, q.o.s)
def test_only_optiondescription():
b = BoolOption('b', '')
raises(SystemError, "a = dumps(b)")
def test_diff_opt_cache(): def test_diff_opt_cache():
b = BoolOption('b', '') b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}]) u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
@ -169,10 +215,7 @@ def test_state_config():
cfg._impl_test = True cfg._impl_test = True
a = dumps(cfg) a = dumps(cfg)
q = loads(a) q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description()) _diff_conf(cfg, q)
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
try: try:
delete_session('29090931') delete_session('29090931')
except ConfigError: except ConfigError:
@ -191,12 +234,9 @@ def test_state_properties():
cfg.cfgimpl_get_settings()[val1].append('test') cfg.cfgimpl_get_settings()[val1].append('test')
a = dumps(cfg) a = dumps(cfg)
q = loads(a) q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description()) _diff_conf(cfg, q)
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
try: try:
delete_session('29090931') delete_session('29090932')
except ConfigError: except ConfigError:
pass pass
@ -212,15 +252,12 @@ def test_state_values():
cfg.val1 = True cfg.val1 = True
a = dumps(cfg) a = dumps(cfg)
q = loads(a) q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description()) _diff_conf(cfg, q)
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
q.val1 = False q.val1 = False
#assert cfg.val1 is True #assert cfg.val1 is True
assert q.val1 is False assert q.val1 is False
try: try:
delete_session('29090931') delete_session('29090933')
except ConfigError: except ConfigError:
pass pass
@ -238,14 +275,94 @@ def test_state_values_owner():
cfg.val1 = True cfg.val1 = True
a = dumps(cfg) a = dumps(cfg)
q = loads(a) q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description()) _diff_conf(cfg, q)
assert cfg.cfgimpl_get_values().get_modified_values() == q.cfgimpl_get_values().get_modified_values()
assert cfg.cfgimpl_get_settings().get_modified_properties() == q.cfgimpl_get_settings().get_modified_properties()
assert cfg.cfgimpl_get_settings().get_modified_permissives() == q.cfgimpl_get_settings().get_modified_permissives()
q.val1 = False q.val1 = False
nval1 = q.cfgimpl_get_description().val1 nval1 = q.cfgimpl_get_description().val1
assert q.getowner(nval1) == owners.newowner assert q.getowner(nval1) == owners.newowner
try: try:
delete_session('29090931') delete_session('29090934')
except ConfigError: except ConfigError:
pass pass
def test_state_metaconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2, session_id='29090935')
conf1._impl_test = True
conf2 = Config(od2, session_id='29090936')
conf2._impl_test = True
meta = MetaConfig([conf1, conf2], session_id='29090937')
meta._impl_test = True
raises(ConfigError, "dumps(meta)")
try:
delete_session('29090935')
delete_session('29090936')
delete_session('29090937')
except ConfigError:
pass
def test_state_groupconfig():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2, session_id='29090935')
conf1._impl_test = True
conf2 = Config(od2, session_id='29090936')
conf2._impl_test = True
meta = GroupConfig([conf1, conf2], session_id='29090937')
meta._impl_test = True
a = dumps(meta)
q = loads(a)
_diff_conf(meta, q)
try:
delete_session('29090935')
delete_session('29090936')
delete_session('29090937')
except ConfigError:
pass
def test_state_unkown_setting_owner():
"""load an unknow _owner, should create it"""
assert not 'supernewuser' in owners.__dict__
loads("""ccopy_reg
_reconstructor
p0
(ctiramisu.setting
Settings
p1
c__builtin__
object
p2
Ntp3
Rp4
(dp5
S'_owner'
p6
S'supernewuser'
p7
sS'_p_'
p8
g0
(ctiramisu.storage.dictionary.setting
Settings
p9
g2
Ntp10
Rp11
(dp12
S'_cache'
p13
(dp14
sS'_permissives'
p15
(dp16
sS'_properties'
p17
(dp18
sbsb.
.""")
assert 'supernewuser' in owners.__dict__

View File

@ -19,15 +19,16 @@
# ____________________________________________________________ # ____________________________________________________________
"enables us to carry out a calculation and return an option's value" "enables us to carry out a calculation and return an option's value"
from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.setting import multitypes
from tiramisu.i18n import _ from tiramisu.i18n import _
# ____________________________________________________________ # ____________________________________________________________
def carry_out_calculation(name, config, callback, callback_params, def carry_out_calculation(option, config, callback, callback_params,
index=None, max_len=None): index=None, max_len=None):
"""a function that carries out a calculation for an option's value """a function that carries out a calculation for an option's value
:param name: the option name (`opt._name`) :param name: the option
:param config: the context config in order to have :param config: the context config in order to have
the whole options available the whole options available
:param callback: the name of the callback function :param callback: the name of the callback function
@ -48,13 +49,13 @@ def carry_out_calculation(name, config, callback, callback_params,
Values could have multiple values only when key is ''. Values could have multiple values only when key is ''.
* if no callback_params: * if no callback_params:
=> calculate() => calculate(<function func at 0x2092320>, [], {})
* if callback_params={'': ('yes',)} * if callback_params={'': ('yes',)}
=> calculate('yes') => calculate(<function func at 0x2092320>, ['yes'], {})
* if callback_params={'value': ('yes',)} * if callback_params={'value': ('yes',)}
=> calculate(value='yes') => calculate(<function func at 0x165b320>, [], {'value': 'yes'})
* if callback_params={'': ('yes', 'no')} * if callback_params={'': ('yes', 'no')}
=> calculate('yes', 'no') => calculate('yes', 'no')
@ -62,58 +63,71 @@ def carry_out_calculation(name, config, callback, callback_params,
* if callback_params={'value': ('yes', 'no')} * if callback_params={'value': ('yes', 'no')}
=> ValueError() => ValueError()
* if callback_params={'': (['yes', 'no'],)}
=> calculate(<function func at 0x176b320>, ['yes', 'no'], {})
* if callback_params={'value': ('yes', 'no')}
=> raises ValueError()
* if callback_params={'': ((opt1, False),)} * if callback_params={'': ((opt1, False),)}
- a simple option: - a simple option:
opt1 == 11 opt1 == 11
=> calculate(11) => calculate(<function func at 0x1cea320>, [11], {})
- a multi option: - a multi option and not master/slave:
opt1 == [1, 2, 3] opt1 == [1, 2, 3]
=> calculate(1) => calculate(<function func at 0x223c320>, [[1, 2, 3]], {})
=> calculate(2)
=> calculate(3) - option is master or slave of opt1:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x223c320>, [1], {})
=> calculate(<function func at 0x223c320>, [2], {})
=> calculate(<function func at 0x223c320>, [3], {})
- opt is a master or slave but not related to option:
opt1 == [1, 2, 3]
=> calculate(<function func at 0x11b0320>, [[1, 2, 3]], {})
* if callback_params={'value': ((opt1, False),)} * if callback_params={'value': ((opt1, False),)}
- a simple option: - a simple option:
opt1 == 11 opt1 == 11
=> calculate(value=11) => calculate(<function func at 0x17ff320>, [], {'value': 11})
- a multi option: - a multi option:
opt1 == [1, 2, 3] opt1 == [1, 2, 3]
=> calculate(value=1) => calculate(<function func at 0x1262320>, [], {'value': [1, 2, 3]})
=> calculate(value=2)
=> calculate(value=3)
* if callback_params={'': ((opt1, False), (opt2, False))} * if callback_params={'': ((opt1, False), (opt2, False))}
- two single options
opt1 = 11
opt2 = 12
=> calculate(<function func at 0x217a320>, [11, 12], {})
- a multi option with a simple option - a multi option with a simple option
opt1 == [1, 2, 3] opt1 == [1, 2, 3]
opt2 == 11 opt2 == 12
=> calculate(1, 11) => calculate(<function func at 0x2153320>, [[1, 2, 3], 12], {})
=> calculate(2, 11)
=> calculate(3, 11)
- a multi option with an other multi option but with same length - a multi option with an other multi option but with same length
opt1 == [1, 2, 3] opt1 == [1, 2, 3]
opt2 == [11, 12, 13] opt2 == [11, 12, 13]
=> calculate(1, 11) => calculate(<function func at 0x1981320>, [[1, 2, 3], [11, 12, 13]], {})
=> calculate(2, 12)
=> calculate(3, 13)
- a multi option with an other multi option but with different length - a multi option with an other multi option but with different length
opt1 == [1, 2, 3] opt1 == [1, 2, 3]
opt2 == [11, 12] opt2 == [11, 12]
=> ConfigError() => calculate(<function func at 0x2384320>, [[1, 2, 3], [11, 12]], {})
- a multi option without value with a simple option - a multi option without value with a simple option
opt1 == [] opt1 == []
opt2 == 11 opt2 == 11
=> [] => calculate(<function func at 0xb65320>, [[], 12], {})
* if callback_params={'value': ((opt1, False), (opt2, False))} * if callback_params={'value': ((opt1, False), (opt2, False))}
=> ConfigError() => raises ValueError()
If index is not None, return a value, otherwise return: If index is not None, return a value, otherwise return:
@ -132,29 +146,36 @@ def carry_out_calculation(name, config, callback, callback_params,
for callbk in callbacks: for callbk in callbacks:
if isinstance(callbk, tuple): if isinstance(callbk, tuple):
# callbk is something link (opt, True|False) # callbk is something link (opt, True|False)
option, force_permissive = callbk opt, force_permissive = callbk
path = config.cfgimpl_get_description().impl_get_path_by_opt( path = config.cfgimpl_get_description().impl_get_path_by_opt(
option) opt)
# get value # get value
try: try:
value = config._getattr(path, force_permissive=True) value = config._getattr(path, force_permissive=True, validate=False)
# convert to list, not modifie this multi
if value.__class__.__name__ == 'Multi':
value = list(value)
except PropertiesOptionError as err: except PropertiesOptionError as err:
if force_permissive: if force_permissive:
continue continue
raise ConfigError(_('unable to carry out a calculation, ' raise ConfigError(_('unable to carry out a calculation, '
'option {0} has properties: {1} for: ' 'option {0} has properties: {1} for: '
'{2}').format(option._name, '{2}').format(opt._name,
err.proptype, err.proptype,
name)) option._name))
is_multi = option.impl_is_multi()
is_multi = False
if opt.impl_is_multi():
#opt is master, search if option is a slave
if opt.impl_get_multitype() == multitypes.master:
if option in opt.impl_get_master_slaves():
is_multi = True
#opt is slave, search if option is an other slaves
elif opt.impl_get_multitype() == multitypes.slave:
if option in opt.impl_get_master_slaves().impl_get_master_slaves():
is_multi = True
if is_multi: if is_multi:
len_value = len(value) len_multi = len(value)
if len_multi is not None and len_multi != len_value:
raise ConfigError(_('unable to carry out a '
'calculation, option value with'
' multi types must have same '
'length for: {0}').format(name))
len_multi = len_value
one_is_multi = True one_is_multi = True
tcparams.setdefault(key, []).append((value, is_multi)) tcparams.setdefault(key, []).append((value, is_multi))
else: else:
@ -167,16 +188,9 @@ def carry_out_calculation(name, config, callback, callback_params,
if one_is_multi: if one_is_multi:
ret = [] ret = []
if index: if index:
if index < len_multi: range_ = [index]
range_ = [index]
else:
range_ = []
ret = None
else: else:
if max_len and max_len < len_multi: range_ = range(len_multi)
range_ = range(max_len)
else:
range_ = range(len_multi)
for incr in range_: for incr in range_:
args = [] args = []
kwargs = {} kwargs = {}
@ -195,10 +209,7 @@ def carry_out_calculation(name, config, callback, callback_params,
if index: if index:
ret = calc ret = calc
else: else:
if isinstance(calc, list): ret.append(calc)
ret.extend(calc)
else:
ret.append(calc)
return ret return ret
else: else:
# no value is multi # no value is multi
@ -212,7 +223,18 @@ def carry_out_calculation(name, config, callback, callback_params,
args.append(couple[0]) args.append(couple[0])
else: else:
kwargs[key] = couple[0] kwargs[key] = couple[0]
return calculate(callback, args, kwargs) ret = calculate(callback, args, kwargs)
if callback_params != {}:
if isinstance(ret, list) and max_len:
ret = ret[:max_len]
if len(ret) < max_len:
ret = ret + [None] * (max_len - len(ret))
if isinstance(ret, list) and index:
if len(ret) < index + 1:
ret = None
else:
ret = ret[index]
return ret
def calculate(callback, args, kwargs): def calculate(callback, args, kwargs):

View File

@ -47,7 +47,7 @@ class SubConfig(object):
:type subpath: `str` with the path name :type subpath: `str` with the path name
""" """
# main option description # main option description
if not isinstance(descr, OptionDescription): if descr is not None and not isinstance(descr, OptionDescription):
raise TypeError(_('descr must be an optiondescription, not {0}' raise TypeError(_('descr must be an optiondescription, not {0}'
).format(type(descr))) ).format(type(descr)))
self._impl_descr = descr self._impl_descr = descr
@ -148,19 +148,25 @@ class SubConfig(object):
except UnicodeEncodeError: except UnicodeEncodeError:
lines.append("{0} = {1}".format(name, lines.append("{0} = {1}".format(name,
value.encode(default_encoding))) value.encode(default_encoding)))
except PropertiesOptionError:
pass
return '\n'.join(lines) return '\n'.join(lines)
__repr__ = __str__ __repr__ = __str__
def _cfgimpl_get_context(self): def _cfgimpl_get_context(self):
return self._impl_context() """context could be None, we need to test it
context is None only if all reference to `Config` object is deleted
(for example we delete a `Config` and we manipulate a reference to
old `SubConfig`, `Values`, `Multi` or `Settings`)
"""
context = self._impl_context()
if context is None:
raise ConfigError(_('the context does not exist anymore'))
return context
def cfgimpl_get_description(self): def cfgimpl_get_description(self):
if self._impl_descr is None: if self._impl_descr is None:
raise ConfigError(_('no option description found for this config' raise ConfigError(_('no option description found for this config'
' (may be metaconfig without meta)')) ' (may be GroupConfig)'))
else: else:
return self._impl_descr return self._impl_descr
@ -249,7 +255,8 @@ class SubConfig(object):
force_properties=force_properties, force_properties=force_properties,
force_permissive=force_permissive) force_permissive=force_permissive)
def find(self, bytype=None, byname=None, byvalue=None, type_='option'): def find(self, bytype=None, byname=None, byvalue=None, type_='option',
check_properties=True):
""" """
finds a list of options recursively in the config finds a list of options recursively in the config
@ -261,11 +268,11 @@ class SubConfig(object):
return self._cfgimpl_get_context()._find(bytype, byname, byvalue, return self._cfgimpl_get_context()._find(bytype, byname, byvalue,
first=False, first=False,
type_=type_, type_=type_,
_subpath=self.cfgimpl_get_path() _subpath=self.cfgimpl_get_path(),
) check_properties=check_properties)
def find_first(self, bytype=None, byname=None, byvalue=None, def find_first(self, bytype=None, byname=None, byvalue=None,
type_='option', display_error=True): type_='option', display_error=True, check_properties=True):
""" """
finds an option recursively in the config finds an option recursively in the config
@ -276,7 +283,8 @@ class SubConfig(object):
""" """
return self._cfgimpl_get_context()._find( return self._cfgimpl_get_context()._find(
bytype, byname, byvalue, first=True, type_=type_, bytype, byname, byvalue, first=True, type_=type_,
_subpath=self.cfgimpl_get_path(), display_error=display_error) _subpath=self.cfgimpl_get_path(), display_error=display_error,
check_properties=check_properties)
def _find(self, bytype, byname, byvalue, first, type_='option', def _find(self, bytype, byname, byvalue, first, type_='option',
_subpath=None, check_properties=True, display_error=True): _subpath=None, check_properties=True, display_error=True):
@ -287,12 +295,9 @@ class SubConfig(object):
:return: find list or an exception if nothing has been found :return: find list or an exception if nothing has been found
""" """
def _filter_by_name(): def _filter_by_name():
try: if byname is None or path == byname or \
if byname is None or path == byname or \ path.endswith('.' + byname):
path.endswith('.' + byname): return True
return True
except IndexError:
pass
return False return False
def _filter_by_value(): def _filter_by_value():
@ -442,23 +447,20 @@ class SubConfig(object):
return pathsvalues return pathsvalues
def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten): def _make_sub_dict(self, opt, path, pathsvalues, _currpath, flatten):
if isinstance(opt, OptionDescription): try:
try: if isinstance(opt, OptionDescription):
pathsvalues += getattr(self, path).make_dict(flatten, pathsvalues += getattr(self, path).make_dict(flatten,
_currpath + _currpath +
path.split('.')) path.split('.'))
except PropertiesOptionError: else:
pass # this just a hidden or disabled option
else:
try:
value = self._getattr(opt._name) value = self._getattr(opt._name)
if flatten: if flatten:
name = opt._name name = opt._name
else: else:
name = '.'.join(_currpath + [opt._name]) name = '.'.join(_currpath + [opt._name])
pathsvalues.append((name, value)) pathsvalues.append((name, value))
except PropertiesOptionError: except PropertiesOptionError:
pass # this just a hidden or disabled option pass
def cfgimpl_get_path(self): def cfgimpl_get_path(self):
descr = self.cfgimpl_get_description() descr = self.cfgimpl_get_description()
@ -466,9 +468,9 @@ class SubConfig(object):
return context_descr.impl_get_path_by_opt(descr) return context_descr.impl_get_path_by_opt(descr)
class CommonConfig(SubConfig): class _CommonConfig(SubConfig):
"abstract base class for the Config and the MetaConfig" "abstract base class for the Config, GroupConfig and the MetaConfig"
__slots__ = ('_impl_values', '_impl_settings', '_impl_meta') __slots__ = ('_impl_values', '_impl_settings', '_impl_meta', '_impl_test')
def _impl_build_all_paths(self): def _impl_build_all_paths(self):
self.cfgimpl_get_description().impl_build_cache() self.cfgimpl_get_description().impl_build_cache()
@ -507,7 +509,8 @@ class CommonConfig(SubConfig):
return None return None
def cfgimpl_get_meta(self): def cfgimpl_get_meta(self):
return self._impl_meta if self._impl_meta is not None:
return self._impl_meta()
# information # information
def impl_set_information(self, key, value): def impl_set_information(self, key, value):
@ -525,11 +528,48 @@ class CommonConfig(SubConfig):
""" """
return self._impl_values.get_information(key, default) return self._impl_values.get_information(key, default)
# ----- state
def __getstate__(self):
if self._impl_meta is not None:
raise ConfigError(_('cannot serialize Config with MetaConfig'))
slots = set()
for subclass in self.__class__.__mro__:
if subclass is not object:
slots.update(subclass.__slots__)
slots -= frozenset(['_impl_context', '_impl_meta', '__weakref__'])
state = {}
for slot in slots:
try:
state[slot] = getattr(self, slot)
except AttributeError:
pass
storage = self._impl_values._p_._storage
if not storage.serializable:
raise ConfigError(_('this storage is not serialisable, could be a '
'none persistent storage'))
state['_storage'] = {'session_id': storage.session_id,
'persistent': storage.persistent}
state['_impl_setting'] = _impl_getstate_setting()
return state
def __setstate__(self, state):
for key, value in state.items():
if key not in ['_storage', '_impl_setting']:
setattr(self, key, value)
set_storage(**state['_impl_setting'])
self._impl_context = weakref.ref(self)
self._impl_settings.context = weakref.ref(self)
self._impl_values.context = weakref.ref(self)
storage = get_storage(test=self._impl_test, **state['_storage'])
self._impl_values._impl_setstate(storage)
self._impl_settings._impl_setstate(storage)
self._impl_meta = None
# ____________________________________________________________ # ____________________________________________________________
class Config(CommonConfig): class Config(_CommonConfig):
"main configuration management entry" "main configuration management entry"
__slots__ = ('__weakref__', '_impl_test') __slots__ = ('__weakref__',)
def __init__(self, descr, session_id=None, persistent=False): def __init__(self, descr, session_id=None, persistent=False):
""" Configuration option management master class """ Configuration option management master class
@ -553,41 +593,6 @@ class Config(CommonConfig):
#undocumented option used only in test script #undocumented option used only in test script
self._impl_test = False self._impl_test = False
def __getstate__(self):
if self._impl_meta is not None:
raise ConfigError('cannot serialize Config with meta')
slots = set()
for subclass in self.__class__.__mro__:
if subclass is not object:
slots.update(subclass.__slots__)
slots -= frozenset(['_impl_context', '__weakref__'])
state = {}
for slot in slots:
try:
state[slot] = getattr(self, slot)
except AttributeError:
pass
storage = self._impl_values._p_._storage
if not storage.serializable:
raise ConfigError('this storage is not serialisable, could be a '
'none persistent storage')
state['_storage'] = {'session_id': storage.session_id,
'persistent': storage.persistent}
state['_impl_setting'] = _impl_getstate_setting()
return state
def __setstate__(self, state):
for key, value in state.items():
if key not in ['_storage', '_impl_setting']:
setattr(self, key, value)
set_storage(**state['_impl_setting'])
self._impl_context = weakref.ref(self)
self._impl_settings.context = weakref.ref(self)
self._impl_values.context = weakref.ref(self)
storage = get_storage(test=self._impl_test, **state['_storage'])
self._impl_values._impl_setstate(storage)
self._impl_settings._impl_setstate(storage)
def cfgimpl_reset_cache(self, def cfgimpl_reset_cache(self,
only_expired=False, only_expired=False,
only=('values', 'settings')): only=('values', 'settings')):
@ -597,115 +602,121 @@ class Config(CommonConfig):
self.cfgimpl_get_settings().reset_cache(only_expired=only_expired) self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
#class MetaConfig(CommonConfig): class GroupConfig(_CommonConfig):
# __slots__ = ('_impl_children',) __slots__ = ('_impl_children', '__weakref__')
# def __init__(self, children, meta=True, session_id=None, persistent=False): def __init__(self, children, session_id=None, persistent=False,
# if not isinstance(children, list): _descr=None):
# raise ValueError(_("metaconfig's children must be a list")) if not isinstance(children, list):
# self._impl_descr = None raise ValueError(_("metaconfig's children must be a list"))
# self._impl_path = None self._impl_children = children
# if meta: settings, values = get_storages(self, session_id, persistent)
# for child in children: self._impl_settings = Settings(self, settings)
# if not isinstance(child, CommonConfig): self._impl_values = Values(self, values)
# raise TypeError(_("metaconfig's children " super(GroupConfig, self).__init__(_descr, weakref.ref(self))
# "must be config, not {0}" self._impl_meta = None
# ).format(type(child))) #undocumented option used only in test script
# if self._impl_descr is None: self._impl_test = False
# self._impl_descr = child.cfgimpl_get_description()
# elif not self._impl_descr is child.cfgimpl_get_description():
# raise ValueError(_('all config in metaconfig must '
# 'have the same optiondescription'))
# if child.cfgimpl_get_meta() is not None:
# raise ValueError(_("child has already a metaconfig's"))
# child._impl_meta = self
# self._impl_children = children def cfgimpl_get_children(self):
# settings, values = get_storages(self, session_id, persistent) return self._impl_children
# self._impl_settings = Settings(self, settings)
# self._impl_values = Values(self, values)
# self._impl_meta = None
# def cfgimpl_get_children(self): #def cfgimpl_get_context(self):
# return self._impl_children # "a meta config is a config which has a setting, that is itself"
# return self
#
def cfgimpl_reset_cache(self,
only_expired=False,
only=('values', 'settings')):
if 'values' in only:
self.cfgimpl_get_values().reset_cache(only_expired=only_expired)
if 'settings' in only:
self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
for child in self._impl_children:
child.cfgimpl_reset_cache(only_expired=only_expired, only=only)
# def cfgimpl_get_context(self): def setattrs(self, path, value):
# "a meta config is a config wich has a setting, that is itself" """Setattr not in current GroupConfig, but in each children
# return self """
for child in self._impl_children:
try:
if not isinstance(child, GroupConfig):
setattr(child, path, value)
else:
child.setattrs(path, value)
except PropertiesOptionError:
pass
# def cfgimpl_reset_cache(self, def find_firsts(self, byname=None, bypath=None, byvalue=None,
# only_expired=False, type_='path', display_error=True):
# only=('values', 'settings')): """Find first not in current GroupConfig, but in each children
# if 'values' in only: """
# self.cfgimpl_get_values().reset_cache(only_expired=only_expired) ret = []
# if 'settings' in only: #if MetaConfig, all children have same OptionDescription as context
# self.cfgimpl_get_settings().reset_cache(only_expired=only_expired) #so search only one time for all children
# for child in self._impl_children: try:
# child.cfgimpl_reset_cache(only_expired=only_expired, only=only) if bypath is None and byname is not None and \
isinstance(self, MetaConfig):
bypath = self._find(bytype=None, byvalue=None, byname=byname,
first=True, type_='path',
check_properties=False,
display_error=display_error)
byname = None
except AttributeError:
pass
for child in self._impl_children:
try:
if not isinstance(child, MetaConfig):
if bypath is not None:
#if byvalue is None, try if not raise
value = getattr(child, bypath)
if byvalue is not None:
if isinstance(value, Multi):
if byvalue in value:
ret.append(child)
else:
if value == byvalue:
ret.append(child)
else:
ret.append(child)
else:
ret.append(child.find_first(byname=byname,
byvalue=byvalue,
type_=type_,
display_error=False))
else:
ret.extend(child.find_firsts(byname=byname,
bypath=bypath,
byvalue=byvalue,
type_=type_,
display_error=False))
except AttributeError:
pass
return self._find_return_results(ret, display_error)
# def set_contexts(self, path, value):
# for child in self._impl_children:
# try:
# if not isinstance(child, MetaConfig):
# setattr(child, path, value)
# else:
# child.set_contexts(path, value)
# except PropertiesOptionError:
# pass
# def find_first_contexts(self, byname=None, bypath=None, byvalue=None, class MetaConfig(GroupConfig):
# type_='path', display_error=True): __slots__ = tuple()
# ret = []
# try: def __init__(self, children, session_id=None, persistent=False):
# if bypath is None and byname is not None and \ descr = None
# self.cfgimpl_get_description() is not None: for child in children:
# bypath = self._find(bytype=None, byvalue=None, byname=byname, if not isinstance(child, _CommonConfig):
# first=True, type_='path', raise TypeError(_("metaconfig's children "
# check_properties=False, "should be config, not {0}"
# display_error=display_error) ).format(type(child)))
# except ConfigError: if child.cfgimpl_get_meta() is not None:
# pass raise ValueError(_("child has already a metaconfig's"))
# for child in self._impl_children: if descr is None:
# try: descr = child.cfgimpl_get_description()
# if not isinstance(child, MetaConfig): elif not descr is child.cfgimpl_get_description():
# if bypath is not None: raise ValueError(_('all config in metaconfig must '
# if byvalue is not None: 'have the same optiondescription'))
# if getattr(child, bypath) == byvalue: child._impl_meta = weakref.ref(self)
# ret.append(child)
# else: super(MetaConfig, self).__init__(children, session_id, persistent, descr)
# #not raise
# getattr(child, bypath)
# ret.append(child)
# else:
# ret.append(child.find_first(byname=byname,
# byvalue=byvalue,
# type_=type_,
# display_error=False))
# else:
# ret.extend(child.find_first_contexts(byname=byname,
# bypath=bypath,
# byvalue=byvalue,
# type_=type_,
# display_error=False))
# except AttributeError:
# pass
# return self._find_return_results(ret, display_error)
def mandatory_warnings(config): def mandatory_warnings(config):
"""convenience function to trace Options that are mandatory and #only for retro-compatibility
where no value has been set config.cfgimpl_get_values().mandatory_warnings()
:returns: generator of mandatory Option's path
"""
#if value in cache, properties are not calculated
config.cfgimpl_reset_cache(only=('values',))
for path in config.cfgimpl_get_description().impl_getpaths(
include_groups=True):
try:
config._getattr(path, force_properties=frozenset(('mandatory',)))
except PropertiesOptionError as err:
if err.proptype == ['mandatory']:
yield path
config.cfgimpl_reset_cache(only=('values',))

View File

@ -39,9 +39,7 @@ forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
def valid_name(name): def valid_name(name):
"an option's name is a str and does not start with 'impl' or 'cfgimpl'" "an option's name is a str and does not start with 'impl' or 'cfgimpl'"
try: if not isinstance(name, str):
name = str(name)
except:
return False return False
if re.match(name_regexp, name) is None and not name.startswith('_') \ if re.match(name_regexp, name) is None and not name.startswith('_') \
and name not in forbidden_names \ and name not in forbidden_names \
@ -337,7 +335,7 @@ class Option(BaseOption):
self._consistencies = None self._consistencies = None
def _launch_consistency(self, func, option, value, context, index, def _launch_consistency(self, func, option, value, context, index,
all_cons_opts): all_cons_opts, warnings_only):
"""Launch consistency now """Launch consistency now
:param func: function name, this name should start with _cons_ :param func: function name, this name should start with _cons_
@ -352,12 +350,11 @@ class Option(BaseOption):
:type index: `int` :type index: `int`
:param all_cons_opts: all options concerne by this consistency :param all_cons_opts: all options concerne by this consistency
:type all_cons_opts: `list` of `tiramisu.option.Option` :type all_cons_opts: `list` of `tiramisu.option.Option`
:param warnings_only: specific raise error for warning
:type warnings_only: `boolean`
""" """
if context is not None: if context is not None:
descr = context.cfgimpl_get_description() descr = context.cfgimpl_get_description()
#option is also in all_cons_opts
if option not in all_cons_opts:
raise ConfigError(_('option not in all_cons_opts'))
all_cons_vals = [] all_cons_vals = []
for opt in all_cons_opts: for opt in all_cons_opts:
@ -368,7 +365,8 @@ class Option(BaseOption):
#if context, calculate value, otherwise get default value #if context, calculate value, otherwise get default value
if context is not None: if context is not None:
opt_value = context._getattr( opt_value = context._getattr(
descr.impl_get_path_by_opt(opt), validate=False) descr.impl_get_path_by_opt(opt), validate=False,
force_permissive=True)
else: else:
opt_value = opt.impl_getdefault() opt_value = opt.impl_getdefault()
@ -382,7 +380,7 @@ class Option(BaseOption):
except IndexError: except IndexError:
#so return if no value #so return if no value
return return
getattr(self, func)(all_cons_opts, all_cons_vals) getattr(self, func)(all_cons_opts, all_cons_vals, warnings_only)
def impl_validate(self, value, context=None, validate=True, def impl_validate(self, value, context=None, validate=True,
force_index=None): force_index=None):
@ -412,7 +410,7 @@ class Option(BaseOption):
else: else:
validator_params = {'': (val,)} validator_params = {'': (val,)}
# Raise ValueError if not valid # Raise ValueError if not valid
carry_out_calculation(self._name, config=context, carry_out_calculation(self, config=context,
callback=self._validator[0], callback=self._validator[0],
callback_params=validator_params) callback_params=validator_params)
@ -420,23 +418,41 @@ class Option(BaseOption):
if _value is None: if _value is None:
return return
# option validation # option validation
self._validate(_value) try:
self._validate(_value)
except ValueError as err:
raise ValueError(_('invalid value for option {0}: {1}'
'').format(self._name, err))
error = None
warning = None
try: try:
# valid with self._validator # valid with self._validator
val_validator(_value) val_validator(_value)
# if not context launch consistency validation self._second_level_validation(_value, self._warnings_only)
if context is not None: except ValueError as error:
descr._valid_consistency(self, _value, context, _index)
self._second_level_validation(_value)
except ValueError as err:
msg = _("invalid value {0} for option {1}: {2}").format(
_value, self._name, err)
if self._warnings_only: if self._warnings_only:
warnings.warn_explicit(ValueWarning(msg, self), warning = error
ValueWarning, error = None
self.__class__.__name__, 0) except ValueWarning as warning:
else: pass
raise ValueError(msg) if error is None and warning is None:
try:
# if context launch consistency validation
if context is not None:
descr._valid_consistency(self, _value, context, _index)
except ValueError as error:
pass
except ValueWarning as warning:
pass
if warning:
msg = _("warning on the value of the option {0}: {1}").format(
self._name, warning)
warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning,
self.__class__.__name__, 0)
elif error:
raise ValueError(_("invalid value for option {0}: {1}").format(
self._name, error))
# generic calculation # generic calculation
if context is not None: if context is not None:
@ -446,17 +462,13 @@ class Option(BaseOption):
do_validation(value, force_index) do_validation(value, force_index)
else: else:
if not isinstance(value, list): if not isinstance(value, list):
raise ValueError(_("which must be a list").format(value, raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self._name))
self._name))
for index, val in enumerate(value): for index, val in enumerate(value):
do_validation(val, index) do_validation(val, index)
def impl_getdefault(self, default_multi=False): def impl_getdefault(self):
"accessing the default value" "accessing the default value"
if not default_multi or not self.impl_is_multi(): return self._default
return self._default
else:
return self.getdefault_multi()
def impl_getdefault_multi(self): def impl_getdefault_multi(self):
"accessing the default value for a multi" "accessing the default value for a multi"
@ -493,7 +505,7 @@ class Option(BaseOption):
def impl_is_multi(self): def impl_is_multi(self):
return self._multi return self._multi
def impl_add_consistency(self, func, *other_opts): def impl_add_consistency(self, func, *other_opts, **params):
"""Add consistency means that value will be validate with other_opts """Add consistency means that value will be validate with other_opts
option's values. option's values.
@ -501,16 +513,18 @@ class Option(BaseOption):
:type func: `str` :type func: `str`
:param other_opts: options used to validate value :param other_opts: options used to validate value
:type other_opts: `list` of `tiramisu.option.Option` :type other_opts: `list` of `tiramisu.option.Option`
:param params: extra params (only warnings_only are allowed)
""" """
if self._consistencies is None: if self._consistencies is None:
self._consistencies = [] self._consistencies = []
warnings_only = params.get('warnings_only', False)
for opt in other_opts: for opt in other_opts:
if not isinstance(opt, Option): if not isinstance(opt, Option):
raise ConfigError(_('consistency should be set with an option')) raise ConfigError(_('consistency must be set with an option'))
if self is opt: if self is opt:
raise ConfigError(_('cannot add consistency with itself')) raise ConfigError(_('cannot add consistency with itself'))
if self.impl_is_multi() != opt.impl_is_multi(): if self.impl_is_multi() != opt.impl_is_multi():
raise ConfigError(_('every options in consistency should be ' raise ConfigError(_('every options in consistency must be '
'multi or none')) 'multi or none'))
func = '_cons_{0}'.format(func) func = '_cons_{0}'.format(func)
all_cons_opts = tuple([self] + list(other_opts)) all_cons_opts = tuple([self] + list(other_opts))
@ -519,19 +533,23 @@ class Option(BaseOption):
if self.impl_is_multi(): if self.impl_is_multi():
for idx, val in enumerate(value): for idx, val in enumerate(value):
self._launch_consistency(func, self, val, None, self._launch_consistency(func, self, val, None,
idx, all_cons_opts) idx, all_cons_opts, warnings_only)
else: else:
self._launch_consistency(func, self, value, None, self._launch_consistency(func, self, value, None,
None, all_cons_opts) None, all_cons_opts, warnings_only)
self._consistencies.append((func, all_cons_opts)) self._consistencies.append((func, all_cons_opts, params))
self.impl_validate(self.impl_getdefault()) self.impl_validate(self.impl_getdefault())
def _cons_not_equal(self, opts, vals): def _cons_not_equal(self, opts, vals, warnings_only):
for idx_inf, val_inf in enumerate(vals): for idx_inf, val_inf in enumerate(vals):
for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]): for idx_sup, val_sup in enumerate(vals[idx_inf + 1:]):
if val_inf == val_sup is not None: if val_inf == val_sup is not None:
raise ValueError(_("same value for {0} and {1}").format( if warnings_only:
opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name)) msg = _("same value for {0} and {1}, should be different")
else:
msg = _("same value for {0} and {1}, must be different")
raise ValueError(msg.format(opts[idx_inf]._name,
opts[idx_inf + idx_sup + 1]._name))
def _impl_convert_callbacks(self, descr, load=False): def _impl_convert_callbacks(self, descr, load=False):
if not load and self._callback is None: if not load and self._callback is None:
@ -587,38 +605,22 @@ class Option(BaseOption):
consistencies = self._state_consistencies consistencies = self._state_consistencies
else: else:
consistencies = self._consistencies consistencies = self._consistencies
if isinstance(consistencies, list): new_value = []
new_value = [] for consistency in consistencies:
for consistency in consistencies: values = []
values = [] for obj in consistency[1]:
for obj in consistency[1]: if load:
if load: values.append(descr.impl_get_opt_by_path(obj))
values.append(descr.impl_get_opt_by_path(obj)) else:
else: values.append(descr.impl_get_path_by_opt(obj))
values.append(descr.impl_get_path_by_opt(obj)) new_value.append((consistency[0], tuple(values), consistency[2]))
new_value.append((consistency[0], tuple(values)))
else:
new_value = {}
for key, _consistencies in consistencies.items():
new_value[key] = []
for key_cons, _cons in _consistencies:
_list_cons = []
for _con in _cons:
if load:
_list_cons.append(
descr.impl_get_opt_by_path(_con))
else:
_list_cons.append(
descr.impl_get_path_by_opt(_con))
new_value[key].append((key_cons, tuple(_list_cons)))
if load: if load:
del(self._state_consistencies) del(self._state_consistencies)
self._consistencies = new_value self._consistencies = new_value
else: else:
self._state_consistencies = new_value self._state_consistencies = new_value
def _second_level_validation(self, value): def _second_level_validation(self, value, warnings_only):
pass pass
@ -663,7 +665,7 @@ class ChoiceOption(Option):
return self._open_values return self._open_values
def _validate(self, value): def _validate(self, value):
if not self._open_values and not value in self._values: if not self.impl_is_openvalues() and not value in self.impl_get_values():
raise ValueError(_('value {0} is not permitted, ' raise ValueError(_('value {0} is not permitted, '
'only {1} is allowed' 'only {1} is allowed'
'').format(value, self._values)) '').format(value, self._values))
@ -676,7 +678,7 @@ class BoolOption(Option):
def _validate(self, value): def _validate(self, value):
if not isinstance(value, bool): if not isinstance(value, bool):
raise ValueError(_('value must be a boolean')) raise ValueError(_('invalid boolean'))
class IntOption(Option): class IntOption(Option):
@ -686,7 +688,7 @@ class IntOption(Option):
def _validate(self, value): def _validate(self, value):
if not isinstance(value, int): if not isinstance(value, int):
raise ValueError(_('value must be an integer')) raise ValueError(_('invalid integer'))
class FloatOption(Option): class FloatOption(Option):
@ -696,7 +698,7 @@ class FloatOption(Option):
def _validate(self, value): def _validate(self, value):
if not isinstance(value, float): if not isinstance(value, float):
raise ValueError(_('value must be a float')) raise ValueError(_('invalid float'))
class StrOption(Option): class StrOption(Option):
@ -706,12 +708,11 @@ class StrOption(Option):
def _validate(self, value): def _validate(self, value):
if not isinstance(value, str): if not isinstance(value, str):
raise ValueError(_('value must be a string, not ' raise ValueError(_('invalid string'))
'{0}').format(type(value)))
if sys.version_info[0] >= 3: if sys.version_info[0] >= 3:
#UnicodeOption is same has StrOption in python 3+ #UnicodeOption is same as StrOption in python 3+
class UnicodeOption(StrOption): class UnicodeOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
pass pass
@ -724,7 +725,7 @@ else:
def _validate(self, value): def _validate(self, value):
if not isinstance(value, unicode): if not isinstance(value, unicode):
raise ValueError(_('value must be an unicode')) raise ValueError(_('invalid unicode'))
class SymLinkOption(BaseOption): class SymLinkOption(BaseOption):
@ -782,17 +783,51 @@ class IPOption(Option):
warnings_only=warnings_only) warnings_only=warnings_only)
def _validate(self, value): def _validate(self, value):
# sometimes an ip term starts with a zero
# but this does not fit in some case, for example bind does not like it
try:
for val in value.split('.'):
if val.startswith("0") and len(val) > 1:
raise ValueError(_('invalid IP'))
except AttributeError:
#if integer for example
raise ValueError(_('invalid IP'))
# 'standard' validation
try: try:
IP('{0}/32'.format(value)) IP('{0}/32'.format(value))
except ValueError: except ValueError:
raise ValueError(_('invalid IP {0}').format(self._name)) raise ValueError(_('invalid IP'))
def _second_level_validation(self, value): def _second_level_validation(self, value, warnings_only):
ip = IP('{0}/32'.format(value)) ip = IP('{0}/32'.format(value))
if not self._allow_reserved and ip.iptype() == 'RESERVED': if not self._allow_reserved and ip.iptype() == 'RESERVED':
raise ValueError(_("IP mustn't not be in reserved class")) if warnings_only:
msg = _("IP shouldn't be in reserved class")
else:
msg = _("invalid IP, mustn't be in reserved class")
raise ValueError(msg)
if self._private_only and not ip.iptype() == 'PRIVATE': if self._private_only and not ip.iptype() == 'PRIVATE':
raise ValueError(_("IP must be in private class")) if warnings_only:
msg = _("IP should be in private class")
else:
msg = _("invalid IP, must be in private class")
raise ValueError(msg)
def _cons_in_network(self, opts, vals, warnings_only):
if len(vals) != 3:
raise ConfigError(_('invalid len for vals'))
if None in vals:
return
ip, network, netmask = vals
if IP(ip) not in IP('{0}/{1}'.format(network, netmask)):
if warnings_only:
msg = _('IP {0} ({1}) not in network {2} ({3}) with netmask {4}'
' ({5})')
else:
msg = _('invalid IP {0} ({1}) not in network {2} ({3}) with '
'netmask {4} ({5})')
raise ValueError(msg.format(ip, opts[0]._name, network,
opts[1]._name, netmask, opts[2]._name))
class PortOption(Option): class PortOption(Option):
@ -852,17 +887,23 @@ class PortOption(Option):
if self._allow_range and ":" in str(value): if self._allow_range and ":" in str(value):
value = str(value).split(':') value = str(value).split(':')
if len(value) != 2: if len(value) != 2:
raise ValueError('range must have two values only') raise ValueError(_('invalid port, range must have two values '
'only'))
if not value[0] < value[1]: if not value[0] < value[1]:
raise ValueError('first port in range must be' raise ValueError(_('invalid port, first port in range must be'
' smaller than the second one') ' smaller than the second one'))
else: else:
value = [value] value = [value]
for val in value: for val in value:
try:
int(val)
except ValueError:
raise ValueError(_('invalid port'))
if not self._min_value <= int(val) <= self._max_value: if not self._min_value <= int(val) <= self._max_value:
raise ValueError('port must be an between {0} and {1}' raise ValueError(_('invalid port, must be an between {0} '
''.format(self._min_value, self._max_value)) 'and {1}').format(self._min_value,
self._max_value))
class NetworkOption(Option): class NetworkOption(Option):
@ -874,12 +915,16 @@ class NetworkOption(Option):
try: try:
IP(value) IP(value)
except ValueError: except ValueError:
raise ValueError(_('invalid network address {0}').format(self._name)) raise ValueError(_('invalid network address'))
def _second_level_validation(self, value): def _second_level_validation(self, value, warnings_only):
ip = IP(value) ip = IP(value)
if ip.iptype() == 'RESERVED': if ip.iptype() == 'RESERVED':
raise ValueError(_("network shall not be in reserved class")) if warnings_only:
msg = _("network address shouldn't be in reserved class")
else:
msg = _("invalid network address, mustn't be in reserved class")
raise ValueError(msg)
class NetmaskOption(Option): class NetmaskOption(Option):
@ -891,21 +936,22 @@ class NetmaskOption(Option):
try: try:
IP('0.0.0.0/{0}'.format(value)) IP('0.0.0.0/{0}'.format(value))
except ValueError: except ValueError:
raise ValueError(_('invalid netmask address {0}').format(self._name)) raise ValueError(_('invalid netmask address'))
def _cons_network_netmask(self, opts, vals): def _cons_network_netmask(self, opts, vals, warnings_only):
#opts must be (netmask, network) options #opts must be (netmask, network) options
if None in vals: if None in vals:
return return
self.__cons_netmask(opts, vals[0], vals[1], False) self.__cons_netmask(opts, vals[0], vals[1], False, warnings_only)
def _cons_ip_netmask(self, opts, vals): def _cons_ip_netmask(self, opts, vals, warnings_only):
#opts must be (netmask, ip) options #opts must be (netmask, ip) options
if None in vals: if None in vals:
return return
self.__cons_netmask(opts, vals[0], vals[1], True) self.__cons_netmask(opts, vals[0], vals[1], True, warnings_only)
def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net): def __cons_netmask(self, opts, val_netmask, val_ipnetwork, make_net,
warnings_only):
if len(opts) != 2: if len(opts) != 2:
raise ConfigError(_('invalid len for opts')) raise ConfigError(_('invalid len for opts'))
msg = None msg = None
@ -918,23 +964,18 @@ class NetmaskOption(Option):
IP('{0}/{1}'.format(val_ipnetwork, val_netmask), IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
make_net=not make_net) make_net=not make_net)
except ValueError: except ValueError:
if not make_net: pass
msg = _("invalid network {0} ({1}) "
"with netmask {2} ({3}),"
" this network is an IP")
else: else:
if make_net: if make_net:
msg = _("invalid IP {0} ({1}) with netmask {2} ({3})," msg = _("invalid IP {0} ({1}) with netmask {2},"
" this IP is a network") " this IP is a network")
except ValueError: except ValueError:
if make_net: if not make_net:
msg = _("invalid IP {0} ({1}) with netmask {2} ({3})") msg = _('invalid network {0} ({1}) with netmask {2}')
else:
msg = _("invalid network {0} ({1}) with netmask {2} ({3})")
if msg is not None: if msg is not None:
raise ValueError(msg.format(val_ipnetwork, opts[1]._name, raise ValueError(msg.format(val_ipnetwork, opts[1]._name,
val_netmask, self._name)) val_netmask))
class BroadcastOption(Option): class BroadcastOption(Option):
@ -945,9 +986,9 @@ class BroadcastOption(Option):
try: try:
IP('{0}/32'.format(value)) IP('{0}/32'.format(value))
except ValueError: except ValueError:
raise ValueError(_('invalid broadcast address {0}').format(self._name)) raise ValueError(_('invalid broadcast address'))
def _cons_broadcast(self, opts, vals): def _cons_broadcast(self, opts, vals, warnings_only):
if len(vals) != 3: if len(vals) != 3:
raise ConfigError(_('invalid len for vals')) raise ConfigError(_('invalid len for vals'))
if None in vals: if None in vals:
@ -967,20 +1008,44 @@ class DomainnameOption(Option):
domainname: domainname:
fqdn: with tld, not supported yet fqdn: with tld, not supported yet
""" """
__slots__ = ('_type', '_allow_ip') __slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
_opt_type = 'domainname' _opt_type = 'domainname'
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, allow_ip=False, type_='domainname', properties=None, allow_ip=False, type_='domainname',
warnings_only=False): warnings_only=False, allow_without_dot=False):
if type_ not in ['netbios', 'hostname', 'domainname']: if type_ not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type_ {0} for hostname').format(type_)) raise ValueError(_('unknown type_ {0} for hostname').format(type_))
self._type = type_ self._type = type_
if allow_ip not in [True, False]: if allow_ip not in [True, False]:
raise ValueError(_('allow_ip must be a boolean')) raise ValueError(_('allow_ip must be a boolean'))
if allow_without_dot not in [True, False]:
raise ValueError(_('allow_without_dot must be a boolean'))
self._allow_ip = allow_ip self._allow_ip = allow_ip
self._allow_without_dot = allow_without_dot
end = ''
extrachar = ''
extrachar_mandatory = ''
if self._type != 'netbios':
allow_number = '\d'
else:
allow_number = ''
if self._type == 'netbios':
length = 14
elif self._type == 'hostname':
length = 62
elif self._type == 'domainname':
length = 62
if allow_without_dot is False:
extrachar_mandatory = '\.'
else:
extrachar = '\.'
end = '+[a-z]*'
self._domain_re = re.compile(r'^(?:[a-z{0}][a-z\d\-{1}]{{,{2}}}{3}){4}$'
''.format(allow_number, extrachar, length,
extrachar_mandatory, end))
super(DomainnameOption, self).__init__(name, doc, default=default, super(DomainnameOption, self).__init__(name, doc, default=default,
default_multi=default_multi, default_multi=default_multi,
callback=callback, callback=callback,
@ -999,29 +1064,94 @@ class DomainnameOption(Option):
return return
except ValueError: except ValueError:
pass pass
if self._type == 'netbios': if self._type == 'domainname' and not self._allow_without_dot and \
length = 15 '.' not in value:
extrachar = '' raise ValueError(_("invalid domainname, must have dot"))
elif self._type == 'hostname': if len(value) > 255:
length = 63 raise ValueError(_("invalid domainname's length (max 255)"))
extrachar = '' if len(value) < 2:
elif self._type == 'domainname': raise ValueError(_("invalid domainname's length (min 2)"))
length = 255 if not self._domain_re.search(value):
extrachar = '\.'
if '.' not in value:
raise ValueError(_("invalid value for {0}, must have dot"
"").format(self._name))
if len(value) > length:
raise ValueError(_("invalid domainname's length for"
" {0} (max {1})").format(self._name, length))
if len(value) == 1:
raise ValueError(_("invalid domainname's length for {0} (min 2)"
"").format(self._name))
regexp = r'^[a-z]([a-z\d{0}-])*[a-z\d]$'.format(extrachar)
if re.match(regexp, value) is None:
raise ValueError(_('invalid domainname')) raise ValueError(_('invalid domainname'))
class EmailOption(DomainnameOption):
__slots__ = tuple()
_opt_type = 'email'
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
def _validate(self, value):
splitted = value.split('@', 1)
try:
username, domain = splitted
except ValueError:
raise ValueError(_('invalid email address, must contains one @'
))
if not self.username_re.search(username):
raise ValueError(_('invalid username in email address'))
super(EmailOption, self)._validate(domain)
class URLOption(DomainnameOption):
__slots__ = tuple()
_opt_type = 'url'
proto_re = re.compile(r'(http|https)://')
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
def _validate(self, value):
match = self.proto_re.search(value)
if not match:
raise ValueError(_('invalid url, must start with http:// or '
'https://'))
value = value[len(match.group(0)):]
# get domain/files
splitted = value.split('/', 1)
try:
domain, files = splitted
except ValueError:
domain = value
files = None
# if port in domain
splitted = domain.split(':', 1)
try:
domain, port = splitted
except ValueError:
domain = splitted[0]
port = 0
if not 0 <= int(port) <= 65535:
raise ValueError(_('invalid url, port must be an between 0 and '
'65536'))
# validate domainname
super(URLOption, self)._validate(domain)
# validate file
if files is not None and files != '' and not self.path_re.search(files):
raise ValueError(_('invalid url, must ends with filename'))
class UsernameOption(Option):
__slots__ = tuple()
_opt_type = 'username'
#regexp build with 'man 8 adduser' informations
username_re = re.compile(r"^[a-z_][a-z0-9_-]{0,30}[$a-z0-9_-]{0,1}$")
def _validate(self, value):
match = self.username_re.search(value)
if not match:
raise ValueError(_('invalid username'))
class FilenameOption(Option):
__slots__ = tuple()
_opt_type = 'file'
path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
def _validate(self, value):
match = self.path_re.search(value)
if not match:
raise ValueError(_('invalid filename'))
class OptionDescription(BaseOption): class OptionDescription(BaseOption):
"""Config's schema (organisation, group) and container of Options """Config's schema (organisation, group) and container of Options
The `OptionsDescription` objects lives in the `tiramisu.config.Config`. The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
@ -1125,11 +1255,12 @@ class OptionDescription(BaseOption):
if not force_no_consistencies and \ if not force_no_consistencies and \
option._consistencies is not None: option._consistencies is not None:
for consistency in option._consistencies: for consistency in option._consistencies:
func, all_cons_opts = consistency func, all_cons_opts, params = consistency
for opt in all_cons_opts: for opt in all_cons_opts:
_consistencies.setdefault(opt, _consistencies.setdefault(opt,
[]).append((func, []).append((func,
all_cons_opts)) all_cons_opts,
params))
else: else:
_currpath.append(attr) _currpath.append(attr)
option.impl_build_cache(cache_path, option.impl_build_cache(cache_path,
@ -1176,7 +1307,6 @@ class OptionDescription(BaseOption):
self._group_type = group_type self._group_type = group_type
if isinstance(group_type, groups.MasterGroupType): if isinstance(group_type, groups.MasterGroupType):
#if master (same name has group) is set #if master (same name has group) is set
identical_master_child_name = False
#for collect all slaves #for collect all slaves
slaves = [] slaves = []
master = None master = None
@ -1193,7 +1323,6 @@ class OptionDescription(BaseOption):
": this option is not a multi" ": this option is not a multi"
"").format(child._name, self._name)) "").format(child._name, self._name))
if child._name == self._name: if child._name == self._name:
identical_master_child_name = True
child._multitype = multitypes.master child._multitype = multitypes.master
master = child master = child
else: else:
@ -1202,14 +1331,18 @@ class OptionDescription(BaseOption):
raise ValueError(_('master group with wrong' raise ValueError(_('master group with wrong'
' master name for {0}' ' master name for {0}'
).format(self._name)) ).format(self._name))
if master._callback is not None and master._callback[1] is not None:
for key, callbacks in master._callback[1].items():
for callbk in callbacks:
if isinstance(callbk, tuple):
if callbk[0] in slaves:
raise ValueError(_("callback of master's option shall "
"not refered a slave's ones"))
master._master_slaves = tuple(slaves) master._master_slaves = tuple(slaves)
for child in self.impl_getchildren(): for child in self.impl_getchildren():
if child != master: if child != master:
child._master_slaves = master child._master_slaves = master
child._multitype = multitypes.slave child._multitype = multitypes.slave
if not identical_master_child_name:
raise ValueError(_("no child has same nom has master group"
" for: {0}").format(self._name))
else: else:
raise ValueError(_('group_type: {0}' raise ValueError(_('group_type: {0}'
' not allowed').format(group_type)) ' not allowed').format(group_type))
@ -1223,15 +1356,20 @@ class OptionDescription(BaseOption):
#consistencies is something like [('_cons_not_equal', (opt1, opt2))] #consistencies is something like [('_cons_not_equal', (opt1, opt2))]
consistencies = self._cache_consistencies.get(option) consistencies = self._cache_consistencies.get(option)
if consistencies is not None: if consistencies is not None:
for func, all_cons_opts in consistencies: for func, all_cons_opts, params in consistencies:
warnings_only = params.get('warnings_only', False)
#all_cons_opts[0] is the option where func is set #all_cons_opts[0] is the option where func is set
ret = all_cons_opts[0]._launch_consistency(func, option, try:
value, all_cons_opts[0]._launch_consistency(func, option,
context, index, value,
all_cons_opts) context, index,
if ret is False: all_cons_opts,
return False warnings_only)
return True except ValueError as err:
if warnings_only:
raise ValueWarning(err.message, option)
else:
raise err
def _impl_getstate(self, descr=None): def _impl_getstate(self, descr=None):
"""enables us to export into a dict """enables us to export into a dict
@ -1341,7 +1479,7 @@ def validate_requires_arg(requires, name):
'must be an option in option {0}').format(name)) 'must be an option in option {0}').format(name))
if option.impl_is_multi(): if option.impl_is_multi():
raise ValueError(_('malformed requirements option {0} ' raise ValueError(_('malformed requirements option {0} '
'should not be a multi').format(name)) 'must not be a multi').format(name))
if expected is not None: if expected is not None:
try: try:
option._validate(expected) option._validate(expected)
@ -1376,17 +1514,17 @@ def validate_requires_arg(requires, name):
def validate_callback(callback, callback_params, type_): def validate_callback(callback, callback_params, type_):
if type(callback) != FunctionType: if type(callback) != FunctionType:
raise ValueError(_('{0} should be a function').format(type_)) raise ValueError(_('{0} must be a function').format(type_))
if callback_params is not None: if callback_params is not None:
if not isinstance(callback_params, dict): if not isinstance(callback_params, dict):
raise ValueError(_('{0}_params should be a dict').format(type_)) raise ValueError(_('{0}_params must be a dict').format(type_))
for key, callbacks in callback_params.items(): for key, callbacks in callback_params.items():
if key != '' and len(callbacks) != 1: if key != '' and len(callbacks) != 1:
raise ValueError(_('{0}_params with key {1} should not have ' raise ValueError(_("{0}_params with key {1} mustn't have "
'length different to 1').format(type_, "length different to 1").format(type_,
key)) key))
if not isinstance(callbacks, tuple): if not isinstance(callbacks, tuple):
raise ValueError(_('{0}_params should be tuple for key "{1}"' raise ValueError(_('{0}_params must be tuple for key "{1}"'
).format(type_, key)) ).format(type_, key))
for callbk in callbacks: for callbk in callbacks:
if isinstance(callbk, tuple): if isinstance(callbk, tuple):
@ -1395,11 +1533,11 @@ def validate_callback(callback, callback_params, type_):
raise ValueError(_('validator not support tuple')) raise ValueError(_('validator not support tuple'))
if not isinstance(option, Option) and not \ if not isinstance(option, Option) and not \
isinstance(option, SymLinkOption): isinstance(option, SymLinkOption):
raise ValueError(_('{0}_params should have an option ' raise ValueError(_('{0}_params must have an option '
'not a {0} for first argument' 'not a {0} for first argument'
).format(type_, type(option))) ).format(type_, type(option)))
if force_permissive not in [True, False]: if force_permissive not in [True, False]:
raise ValueError(_('{0}_params should have a boolean' raise ValueError(_('{0}_params must have a boolean'
' not a {0} for second argument' ' not a {0} for second argument'
).format(type_, type( ).format(type_, type(
force_permissive))) force_permissive)))

View File

@ -19,7 +19,7 @@ from time import time
from copy import copy from copy import copy
import weakref import weakref
from tiramisu.error import (RequirementError, PropertiesOptionError, from tiramisu.error import (RequirementError, PropertiesOptionError,
ConstError) ConstError, ConfigError)
from tiramisu.i18n import _ from tiramisu.i18n import _
@ -237,6 +237,14 @@ multitypes = MultiTypeModule()
populate_multitypes() populate_multitypes()
# ____________________________________________________________
class Undefined():
pass
undefined = Undefined()
# ____________________________________________________________ # ____________________________________________________________
class Property(object): class Property(object):
"a property is responsible of the option's value access rules" "a property is responsible of the option's value access rules"
@ -249,6 +257,11 @@ class Property(object):
self._properties = prop self._properties = prop
def append(self, propname): def append(self, propname):
"""Appends a property named propname
:param propname: a predefined or user defined property name
:type propname: string
"""
if self._opt is not None and self._opt._calc_properties is not None \ if self._opt is not None and self._opt._calc_properties is not None \
and propname in self._opt._calc_properties: and propname in self._opt._calc_properties:
raise ValueError(_('cannot append {0} property for option {1}: ' raise ValueError(_('cannot append {0} property for option {1}: '
@ -258,12 +271,29 @@ class Property(object):
self._setting._setproperties(self._properties, self._opt, self._path) self._setting._setproperties(self._properties, self._opt, self._path)
def remove(self, propname): def remove(self, propname):
"""Removes a property named propname
:param propname: a predefined or user defined property name
:type propname: string
"""
if propname in self._properties: if propname in self._properties:
self._properties.remove(propname) self._properties.remove(propname)
self._setting._setproperties(self._properties, self._opt, self._setting._setproperties(self._properties, self._opt,
self._path) self._path)
def extend(self, propnames):
"""Extends properties to the existing properties
:param propnames: an iterable made of property names
:type propnames: iterable of string
"""
for propname in propnames:
self.append(propname)
def reset(self): def reset(self):
"""resets the properties (does not **clear** the properties,
default properties are still present)
"""
self._setting.reset(_path=self._path) self._setting.reset(_path=self._path)
def __contains__(self, propname): def __contains__(self, propname):
@ -275,7 +305,7 @@ class Property(object):
#____________________________________________________________ #____________________________________________________________
class Settings(object): class Settings(object):
"``Config()``'s configuration options" "``config.Config()``'s configuration options settings"
__slots__ = ('context', '_owner', '_p_', '__weakref__') __slots__ = ('context', '_owner', '_p_', '__weakref__')
def __init__(self, context, storage): def __init__(self, context, storage):
@ -293,6 +323,17 @@ class Settings(object):
self.context = weakref.ref(context) self.context = weakref.ref(context)
self._p_ = storage self._p_ = storage
def _getcontext(self):
"""context could be None, we need to test it
context is None only if all reference to `Config` object is deleted
(for example we delete a `Config` and we manipulate a reference to
old `SubConfig`, `Values`, `Multi` or `Settings`)
"""
context = self.context()
if context is None:
raise ConfigError(_('the context does not exist anymore'))
return context
#____________________________________________________________ #____________________________________________________________
# properties methods # properties methods
def __contains__(self, propname): def __contains__(self, propname):
@ -317,16 +358,16 @@ class Settings(object):
raise ValueError(_('opt and all_properties must not be set ' raise ValueError(_('opt and all_properties must not be set '
'together in reset')) 'together in reset'))
if all_properties: if all_properties:
self._p_.reset_all_propertives() self._p_.reset_all_properties()
else: else:
if opt is not None and _path is None: if opt is not None and _path is None:
_path = self._get_path_by_opt(opt) _path = self._get_path_by_opt(opt)
self._p_.reset_properties(_path) self._p_.reset_properties(_path)
self.context().cfgimpl_reset_cache() self._getcontext().cfgimpl_reset_cache()
def _getproperties(self, opt=None, path=None, is_apply_req=True): def _getproperties(self, opt=None, path=None, is_apply_req=True):
if opt is None: if opt is None:
props = self._p_.getproperties(path, default_properties) props = copy(self._p_.getproperties(path, default_properties))
else: else:
if path is None: if path is None:
raise ValueError(_('if opt is not None, path should not be' raise ValueError(_('if opt is not None, path should not be'
@ -337,8 +378,8 @@ class Settings(object):
ntime = int(time()) ntime = int(time())
is_cached, props = self._p_.getcache(path, ntime) is_cached, props = self._p_.getcache(path, ntime)
if is_cached: if is_cached:
return props return copy(props)
props = self._p_.getproperties(path, opt._properties) props = copy(self._p_.getproperties(path, opt._properties))
if is_apply_req: if is_apply_req:
props |= self.apply_requires(opt, path) props |= self.apply_requires(opt, path)
if 'cache' in self: if 'cache' in self:
@ -346,7 +387,7 @@ class Settings(object):
if ntime is None: if ntime is None:
ntime = int(time()) ntime = int(time())
ntime = ntime + expires_time ntime = ntime + expires_time
self._p_.setcache(path, props, ntime) self._p_.setcache(path, copy(props), ntime)
return props return props
def append(self, propname): def append(self, propname):
@ -362,6 +403,10 @@ class Settings(object):
props.remove(propname) props.remove(propname)
self._setproperties(props, None, None) self._setproperties(props, None, None)
def extend(self, propnames):
for propname in propnames:
self.append(propname)
def _setproperties(self, properties, opt, path): def _setproperties(self, properties, opt, path):
"""save properties for specified opt """save properties for specified opt
(never save properties if same has option properties) (never save properties if same has option properties)
@ -375,7 +420,7 @@ class Settings(object):
self._p_.reset_properties(path) self._p_.reset_properties(path)
else: else:
self._p_.setproperties(path, properties) self._p_.setproperties(path, properties)
self.context().cfgimpl_reset_cache() self._getcontext().cfgimpl_reset_cache()
#____________________________________________________________ #____________________________________________________________
def validate_properties(self, opt_or_descr, is_descr, is_write, path, def validate_properties(self, opt_or_descr, is_descr, is_write, path,
@ -400,11 +445,13 @@ class Settings(object):
(typically with the `frozen` property) (typically with the `frozen` property)
""" """
# opt properties # opt properties
properties = copy(self._getproperties(opt_or_descr, path)) properties = self._getproperties(opt_or_descr, path)
self_properties = self._getproperties()
# remove opt permissive # remove opt permissive
# permissive affect option's permission with or without permissive
# global property
properties -= self._p_.getpermissive(path) properties -= self._p_.getpermissive(path)
# remove global permissive if need # remove global permissive if need
self_properties = copy(self._getproperties())
if force_permissive is True or 'permissive' in self_properties: if force_permissive is True or 'permissive' in self_properties:
properties -= self._p_.getpermissive() properties -= self._p_.getpermissive()
if force_permissives is not None: if force_permissives is not None:
@ -421,7 +468,7 @@ class Settings(object):
properties -= frozenset(('mandatory', 'frozen')) properties -= frozenset(('mandatory', 'frozen'))
else: else:
if 'mandatory' in properties and \ if 'mandatory' in properties and \
not self.context().cfgimpl_get_values()._isempty( not self._getcontext().cfgimpl_get_values()._isempty(
opt_or_descr, value): opt_or_descr, value):
properties.remove('mandatory') properties.remove('mandatory')
if is_write and 'everything_frozen' in self_properties: if is_write and 'everything_frozen' in self_properties:
@ -544,6 +591,7 @@ class Settings(object):
# filters the callbacks # filters the callbacks
calc_properties = set() calc_properties = set()
context = self._getcontext()
for requires in opt._requires: for requires in opt._requires:
for require in requires: for require in requires:
option, expected, action, inverse, \ option, expected, action, inverse, \
@ -555,8 +603,7 @@ class Settings(object):
" '{0}' with requirement on: " " '{0}' with requirement on: "
"'{1}'").format(path, reqpath)) "'{1}'").format(path, reqpath))
try: try:
value = self.context()._getattr(reqpath, value = context._getattr(reqpath, force_permissive=True)
force_permissive=True)
except PropertiesOptionError as err: except PropertiesOptionError as err:
if not transitive: if not transitive:
continue continue
@ -585,7 +632,7 @@ class Settings(object):
:param opt: `Option`'s object :param opt: `Option`'s object
:returns: path :returns: path
""" """
return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt) return self._getcontext().cfgimpl_get_description().impl_get_path_by_opt(opt)
def get_modified_properties(self): def get_modified_properties(self):
return self._p_.get_modified_properties() return self._p_.get_modified_properties()

View File

@ -29,7 +29,7 @@ class Settings(Cache):
self._permissives = {} self._permissives = {}
super(Settings, self).__init__(storage) super(Settings, self).__init__(storage)
# propertives # properties
def setproperties(self, path, properties): def setproperties(self, path, properties):
self._properties[path] = properties self._properties[path] = properties
@ -39,7 +39,7 @@ class Settings(Cache):
def hasproperties(self, path): def hasproperties(self, path):
return path in self._properties return path in self._properties
def reset_all_propertives(self): def reset_all_properties(self):
self._properties.clear() self._properties.clear()
def reset_properties(self, path): def reset_properties(self, path):

View File

@ -31,7 +31,7 @@ class Settings(Sqlite3DB):
self._storage.execute(settings_table, commit=False) self._storage.execute(settings_table, commit=False)
self._storage.execute(permissives_table) self._storage.execute(permissives_table)
# propertives # properties
def setproperties(self, path, properties): def setproperties(self, path, properties):
path = self._sqlite_encode_path(path) path = self._sqlite_encode_path(path)
self._storage.execute("DELETE FROM property WHERE path = ?", (path,), self._storage.execute("DELETE FROM property WHERE path = ?", (path,),
@ -54,7 +54,7 @@ class Settings(Sqlite3DB):
return self._storage.select("SELECT properties FROM property WHERE " return self._storage.select("SELECT properties FROM property WHERE "
"path = ?", (path,)) is not None "path = ?", (path,)) is not None
def reset_all_propertives(self): def reset_all_properties(self):
self._storage.execute("DELETE FROM property") self._storage.execute("DELETE FROM property")
def reset_properties(self, path): def reset_properties(self, path):

View File

@ -19,8 +19,8 @@ from time import time
from copy import copy from copy import copy
import sys import sys
import weakref import weakref
from tiramisu.error import ConfigError, SlaveError from tiramisu.error import ConfigError, SlaveError, PropertiesOptionError
from tiramisu.setting import owners, multitypes, expires_time from tiramisu.setting import owners, multitypes, expires_time, undefined
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
from tiramisu.i18n import _ from tiramisu.i18n import _
from tiramisu.option import SymLinkOption from tiramisu.option import SymLinkOption
@ -44,15 +44,28 @@ class Values(object):
# the storage type is dictionary or sqlite3 # the storage type is dictionary or sqlite3
self._p_ = storage self._p_ = storage
def _getcontext(self):
"""context could be None, we need to test it
context is None only if all reference to `Config` object is deleted
(for example we delete a `Config` and we manipulate a reference to
old `SubConfig`, `Values`, `Multi` or `Settings`)
"""
context = self.context()
if context is None:
raise ConfigError(_('the context does not exist anymore'))
return context
def _getdefault(self, opt): def _getdefault(self, opt):
""" """
actually retrieves the default value actually retrieves the default value
:param opt: the `option.Option()` object :param opt: the `option.Option()` object
""" """
meta = self.context().cfgimpl_get_meta() meta = self._getcontext().cfgimpl_get_meta()
if meta is not None: if meta is not None:
value = meta.cfgimpl_get_values()[opt] value = meta.cfgimpl_get_values()[opt]
if isinstance(value, Multi):
value = list(value)
else: else:
value = opt.impl_getdefault() value = opt.impl_getdefault()
if opt.impl_is_multi(): if opt.impl_is_multi():
@ -60,7 +73,7 @@ class Values(object):
else: else:
return value return value
def _getvalue(self, opt, path, validate=True): def _getvalue(self, opt, path):
"""actually retrieves the value """actually retrieves the value
:param opt: the `option.Option()` object :param opt: the `option.Option()` object
@ -69,14 +82,9 @@ class Values(object):
if not self._p_.hasvalue(path): if not self._p_.hasvalue(path):
# if there is no value # if there is no value
value = self._getdefault(opt) value = self._getdefault(opt)
if opt.impl_is_multi():
value = Multi(value, self.context, opt, path, validate)
else: else:
# if there is a value # if there is a value
value = self._p_.getvalue(path) value = self._p_.getvalue(path)
if opt.impl_is_multi() and not isinstance(value, Multi):
# load value so don't need to validate if is not a Multi
value = Multi(value, self.context, opt, path, validate=False)
return value return value
def get_modified_values(self): def get_modified_values(self):
@ -103,11 +111,11 @@ class Values(object):
if path is None: if path is None:
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() context = self._getcontext()
setting = context.cfgimpl_get_settings()
opt.impl_validate(opt.impl_getdefault(), opt.impl_validate(opt.impl_getdefault(),
self.context(), context, 'validator' in setting)
'validator' in setting) 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):
for slave in opt.impl_get_master_slaves(): for slave in opt.impl_get_master_slaves():
@ -135,7 +143,7 @@ class Values(object):
callback, callback_params = opt._callback callback, callback_params = opt._callback
if callback_params is None: if callback_params is None:
callback_params = {} callback_params = {}
return carry_out_calculation(opt._name, config=self.context(), return carry_out_calculation(opt, config=self._getcontext(),
callback=callback, callback=callback,
callback_params=callback_params, callback_params=callback_params,
index=index, max_len=max_len) index=index, max_len=max_len)
@ -149,7 +157,7 @@ class Values(object):
if path is None: if path is None:
path = self._get_opt_path(opt) path = self._get_opt_path(opt)
ntime = None ntime = None
setting = self.context().cfgimpl_get_settings() setting = self._getcontext().cfgimpl_get_settings()
if 'cache' in setting and self._p_.hascache(path): if 'cache' in setting and self._p_.hascache(path):
if 'expire' in setting: if 'expire' in setting:
ntime = int(time()) ntime = int(time())
@ -174,7 +182,8 @@ class Values(object):
def _getitem(self, opt, path, validate, force_permissive, force_properties, def _getitem(self, opt, path, validate, force_permissive, force_properties,
validate_properties): validate_properties):
# options with callbacks # options with callbacks
setting = self.context().cfgimpl_get_settings() context = self._getcontext()
setting = context.cfgimpl_get_settings()
is_frozen = 'frozen' in setting[opt] is_frozen = 'frozen' in setting[opt]
# For calculating properties, we need value (ie for mandatory value). # For calculating properties, we need value (ie for mandatory value).
# If value is calculating with a PropertiesOptionError's option # If value is calculating with a PropertiesOptionError's option
@ -184,7 +193,7 @@ class Values(object):
# ConfigError if properties did not raise. # ConfigError if properties did not raise.
config_error = None config_error = None
force_permissives = None force_permissives = None
# if value is callback and is not set # if value has callback and is not set
# or frozen with force_default_on_freeze # or frozen with force_default_on_freeze
if opt.impl_has_callback() and ( if opt.impl_has_callback() and (
self._is_default_owner(path) or self._is_default_owner(path) or
@ -194,7 +203,7 @@ class Values(object):
if (opt.impl_is_multi() and if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.slave): opt.impl_get_multitype() == multitypes.slave):
masterp = self._get_opt_path(opt.impl_get_master_slaves()) masterp = self._get_opt_path(opt.impl_get_master_slaves())
mastervalue = getattr(self.context(), masterp) mastervalue = context._getattr(masterp, validate=validate)
lenmaster = len(mastervalue) lenmaster = len(mastervalue)
if lenmaster == 0: if lenmaster == 0:
value = [] value = []
@ -226,9 +235,12 @@ class Values(object):
if opt.impl_is_multi(): if opt.impl_is_multi():
value = Multi(value, self.context, opt, path, validate) value = Multi(value, self.context, opt, path, validate)
else: else:
value = self._getvalue(opt, path, validate) value = self._getvalue(opt, path)
if opt.impl_is_multi():
# load value so don't need to validate if is not a Multi
value = Multi(value, self.context, opt, path, validate=validate)
if config_error is None and validate: if config_error is None and validate:
opt.impl_validate(value, self.context(), 'validator' in setting) opt.impl_validate(value, context, 'validator' in setting)
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)
@ -249,24 +261,45 @@ 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
opt.impl_validate(value, self.context(), context = self._getcontext()
'validator' in self.context().cfgimpl_get_settings()) opt.impl_validate(value, context,
if opt.impl_is_multi() and not isinstance(value, Multi): 'validator' in context.cfgimpl_get_settings())
if opt.impl_is_multi():
value = Multi(value, self.context, opt, path, setitem=True) value = Multi(value, self.context, opt, path, setitem=True)
# Save old value
if opt.impl_get_multitype() == multitypes.master and \
self._p_.hasvalue(path):
old_value = self._p_.getvalue(path)
old_owner = self._p_.getowner(path, None)
else:
old_value = undefined
old_owner = undefined
self._setvalue(opt, path, value, force_permissive=force_permissive, self._setvalue(opt, path, value, force_permissive=force_permissive,
is_write=is_write) is_write=is_write)
if opt.impl_is_multi() and opt.impl_get_multitype() == multitypes.master:
try:
value._valid_master()
except Exception, err:
if old_value is not undefined:
self._p_.setvalue(path, old_value, old_owner)
else:
self._p_.resetvalue(path)
raise err
def _setvalue(self, opt, path, value, force_permissive=False, def _setvalue(self, opt, path, value, force_permissive=False,
force_properties=None, force_properties=None,
is_write=True, validate_properties=True): is_write=True, validate_properties=True):
self.context().cfgimpl_reset_cache() context = self._getcontext()
context.cfgimpl_reset_cache()
if validate_properties: if validate_properties:
setting = self.context().cfgimpl_get_settings() setting = context.cfgimpl_get_settings()
setting.validate_properties(opt, False, is_write, setting.validate_properties(opt, False, is_write,
value=value, path=path, value=value, path=path,
force_permissive=force_permissive, force_permissive=force_permissive,
force_properties=force_properties) force_properties=force_properties)
owner = self.context().cfgimpl_get_settings().getowner() owner = context.cfgimpl_get_settings().getowner()
if isinstance(value, Multi):
value = list(value)
self._p_.setvalue(path, value, owner) self._p_.setvalue(path, value, owner)
def getowner(self, opt): def getowner(self, opt):
@ -283,7 +316,7 @@ class Values(object):
def _getowner(self, path): def _getowner(self, path):
owner = self._p_.getowner(path, owners.default) owner = self._p_.getowner(path, owners.default)
meta = self.context().cfgimpl_get_meta() meta = self._getcontext().cfgimpl_get_meta()
if owner is owners.default and meta is not None: if owner is owners.default and meta is not None:
owner = meta.cfgimpl_get_values()._getowner(path) owner = meta.cfgimpl_get_values()._getowner(path)
return owner return owner
@ -335,7 +368,7 @@ class Values(object):
:param opt: the `option.Option` object :param opt: the `option.Option` object
:returns: a string with points like "gc.dummy.my_option" :returns: a string with points like "gc.dummy.my_option"
""" """
return self.context().cfgimpl_get_description().impl_get_path_by_opt(opt) return self._getcontext().cfgimpl_get_description().impl_get_path_by_opt(opt)
# information # information
def set_information(self, key, value): def set_information(self, key, value):
@ -360,6 +393,42 @@ class Values(object):
raise ValueError(_("information's item" raise ValueError(_("information's item"
" not found: {0}").format(key)) " not found: {0}").format(key))
def mandatory_warnings(self):
"""convenience function to trace Options that are mandatory and
where no value has been set
:returns: generator of mandatory Option's path
"""
#if value in cache, properties are not calculated
self.reset_cache(False)
context = self.context()
for path in context.cfgimpl_get_description().impl_getpaths(
include_groups=True):
try:
context._getattr(path,
force_properties=frozenset(('mandatory',)))
except PropertiesOptionError as err:
if err.proptype == ['mandatory']:
yield path
self.reset_cache(False)
def force_cache(self):
"""parse all option to force data in cache
"""
context = self.context()
if not 'cache' in context.cfgimpl_get_settings():
raise ConfigError(_('can force cache only if cache '
'is actived in config'))
#remove all cached properties and value to update "expired" time
context.cfgimpl_reset_cache()
for path in context.cfgimpl_get_description().impl_getpaths(
include_groups=True):
try:
context._getattr(path)
except PropertiesOptionError:
pass
def __getstate__(self): def __getstate__(self):
return {'_p_': self._p_} return {'_p_': self._p_}
@ -387,6 +456,8 @@ class Multi(list):
:param opt: the option object that have this Multi value :param opt: the option object that have this Multi value
:param setitem: only if set a value :param setitem: only if set a value
""" """
if isinstance(value, Multi):
raise ValueError(_('{0} is already a Multi ').format(opt._name))
self.opt = opt self.opt = opt
self.path = path self.path = path
if not isinstance(context, weakref.ReferenceType): if not isinstance(context, weakref.ReferenceType):
@ -396,21 +467,32 @@ class Multi(list):
value = [value] value = [value]
if validate and self.opt.impl_get_multitype() == multitypes.slave: if validate and self.opt.impl_get_multitype() == multitypes.slave:
value = self._valid_slave(value, setitem) value = self._valid_slave(value, setitem)
elif validate and self.opt.impl_get_multitype() == multitypes.master: elif not setitem and validate and \
self._valid_master(value) self.opt.impl_get_multitype() == multitypes.master:
self._valid_master()
super(Multi, self).__init__(value) super(Multi, self).__init__(value)
def _getcontext(self):
"""context could be None, we need to test it
context is None only if all reference to `Config` object is deleted
(for example we delete a `Config` and we manipulate a reference to
old `SubConfig`, `Values`, `Multi` or `Settings`)
"""
context = self.context()
if context is None:
raise ConfigError(_('the context does not exist anymore'))
return context
def _valid_slave(self, value, setitem): def _valid_slave(self, value, setitem):
#if slave, had values until master's one #if slave, had values until master's one
values = self.context().cfgimpl_get_values() context = self._getcontext()
masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt( values = context.cfgimpl_get_values()
masterp = context.cfgimpl_get_description().impl_get_path_by_opt(
self.opt.impl_get_master_slaves()) self.opt.impl_get_master_slaves())
mastervalue = getattr(self.context(), masterp) mastervalue = context._getattr(masterp, validate=False)
masterlen = len(mastervalue) masterlen = len(mastervalue)
valuelen = len(value) valuelen = len(value)
is_default_owner = not values._is_default_owner(self.path) or setitem if valuelen > masterlen or (valuelen < masterlen and setitem):
if valuelen > masterlen or (valuelen < masterlen and
is_default_owner):
raise SlaveError(_("invalid len for the slave: {0}" raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format( " which has {1} as master").format(
self.opt._name, masterp)) self.opt._name, masterp))
@ -427,59 +509,43 @@ class Multi(list):
#else: same len so do nothing #else: same len so do nothing
return value return value
def _valid_master(self, value): def _valid_master(self):
masterlen = len(value) #masterlen = len(value)
values = self.context().cfgimpl_get_values() values = self._getcontext().cfgimpl_get_values()
for slave in self.opt._master_slaves: for slave in self.opt._master_slaves:
path = values._get_opt_path(slave) path = values._get_opt_path(slave)
if not values._is_default_owner(path): Multi(values._getvalue(slave, path), self.context, slave, path)
value_slave = values._getvalue(slave, path)
if len(value_slave) > masterlen:
raise SlaveError(_("invalid len for the master: {0}"
" which has {1} as slave with"
" greater len").format(
self.opt._name, slave._name))
elif len(value_slave) < masterlen:
for num in range(0, masterlen - len(value_slave)):
if slave.impl_has_callback():
# if callback add a value, but this value will not
# change anymore automaticly (because this value
# has owner)
index = value_slave.__len__()
value_slave.append(
values._getcallback_value(slave, index=index),
force=True)
else:
value_slave.append(slave.impl_getdefault_multi(),
force=True)
def __setitem__(self, index, value): def __setitem__(self, index, value):
self._validate(value, index) self._validate(value, index)
#assume not checking mandatory property #assume not checking mandatory property
super(Multi, self).__setitem__(index, value) super(Multi, self).__setitem__(index, value)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
def append(self, value, force=False): def append(self, value=undefined, force=False):
"""the list value can be updated (appened) """the list value can be updated (appened)
only if the option is a master only if the option is a master
""" """
context = self._getcontext()
if not force: if not force:
if self.opt.impl_get_multitype() == multitypes.slave: if self.opt.impl_get_multitype() == multitypes.slave:
raise SlaveError(_("cannot append a value on a multi option {0}" raise SlaveError(_("cannot append a value on a multi option {0}"
" which is a slave").format(self.opt._name)) " which is a slave").format(self.opt._name))
elif self.opt.impl_get_multitype() == multitypes.master: elif self.opt.impl_get_multitype() == multitypes.master:
values = self.context().cfgimpl_get_values() values = context.cfgimpl_get_values()
if value is None and self.opt.impl_has_callback(): if value is undefined and self.opt.impl_has_callback():
value = values._getcallback_value(self.opt) value = values._getcallback_value(self.opt)
#Force None il return a list #Force None il return a list
if isinstance(value, list): if isinstance(value, list):
value = None value = None
index = self.__len__() index = self.__len__()
if value is undefined:
value = self.opt.impl_getdefault_multi()
self._validate(value, index) self._validate(value, index)
super(Multi, self).append(value) super(Multi, self).append(value)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, context.cfgimpl_get_values()._setvalue(self.opt, self.path,
self, self,
validate_properties=not force) validate_properties=not force)
if not force and self.opt.impl_get_multitype() == multitypes.master: if not force and self.opt.impl_get_multitype() == multitypes.master:
for slave in self.opt.impl_get_master_slaves(): for slave in self.opt.impl_get_master_slaves():
path = values._get_opt_path(slave) path = values._get_opt_path(slave)
@ -488,16 +554,15 @@ class Multi(list):
dvalue = values._getcallback_value(slave, index=index) dvalue = values._getcallback_value(slave, index=index)
else: else:
dvalue = slave.impl_getdefault_multi() dvalue = slave.impl_getdefault_multi()
old_value = values.getitem(slave, path, old_value = values.getitem(slave, path, validate=False,
validate_properties=False) validate_properties=False)
if len(old_value) < self.__len__(): if len(old_value) + 1 != self.__len__():
values.getitem(slave, path, raise SlaveError(_("invalid len for the slave: {0}"
validate_properties=False).append( " which has {1} as master").format(
dvalue, force=True) self.opt._name, self.__len__()))
else: values.getitem(slave, path, validate=False,
values.getitem(slave, path, validate_properties=False).append(
validate_properties=False)[ dvalue, force=True)
index] = dvalue
def sort(self, cmp=None, key=None, reverse=False): def sort(self, cmp=None, key=None, reverse=False):
if self.opt.impl_get_multitype() in [multitypes.slave, if self.opt.impl_get_multitype() in [multitypes.slave,
@ -510,7 +575,7 @@ class Multi(list):
super(Multi, self).sort(key=key, reverse=reverse) super(Multi, self).sort(key=key, reverse=reverse)
else: else:
super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse) super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
def reverse(self): def reverse(self):
if self.opt.impl_get_multitype() in [multitypes.slave, if self.opt.impl_get_multitype() in [multitypes.slave,
@ -518,7 +583,7 @@ class Multi(list):
raise SlaveError(_("cannot reverse multi option {0} if master or " raise SlaveError(_("cannot reverse multi option {0} if master or "
"slave").format(self.opt._name)) "slave").format(self.opt._name))
super(Multi, self).reverse() super(Multi, self).reverse()
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
def insert(self, index, obj): def insert(self, index, obj):
if self.opt.impl_get_multitype() in [multitypes.slave, if self.opt.impl_get_multitype() in [multitypes.slave,
@ -526,7 +591,7 @@ class Multi(list):
raise SlaveError(_("cannot insert multi option {0} if master or " raise SlaveError(_("cannot insert multi option {0} if master or "
"slave").format(self.opt._name)) "slave").format(self.opt._name))
super(Multi, self).insert(index, obj) super(Multi, self).insert(index, obj)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
def extend(self, iterable): def extend(self, iterable):
if self.opt.impl_get_multitype() in [multitypes.slave, if self.opt.impl_get_multitype() in [multitypes.slave,
@ -534,12 +599,12 @@ class Multi(list):
raise SlaveError(_("cannot extend multi option {0} if master or " raise SlaveError(_("cannot extend multi option {0} if master or "
"slave").format(self.opt._name)) "slave").format(self.opt._name))
super(Multi, self).extend(iterable) super(Multi, self).extend(iterable)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self) self._getcontext().cfgimpl_get_values()._setvalue(self.opt, self.path, self)
def _validate(self, value, force_index): def _validate(self, value, force_index):
if value is not None: if value is not None:
try: try:
self.opt.impl_validate(value, context=self.context(), self.opt.impl_validate(value, context=self._getcontext(),
force_index=force_index) force_index=force_index)
except ValueError as err: except ValueError as err:
raise ValueError(_("invalid value {0} " raise ValueError(_("invalid value {0} "
@ -557,19 +622,20 @@ class Multi(list):
:type force: boolean :type force: boolean
:returns: item at index :returns: item at index
""" """
context = self._getcontext()
if not force: if not force:
if self.opt.impl_get_multitype() == multitypes.slave: if self.opt.impl_get_multitype() == multitypes.slave:
raise SlaveError(_("cannot pop a value on a multi option {0}" raise SlaveError(_("cannot pop a value on a multi option {0}"
" which is a slave").format(self.opt._name)) " which is a slave").format(self.opt._name))
elif self.opt.impl_get_multitype() == multitypes.master: if self.opt.impl_get_multitype() == multitypes.master:
for slave in self.opt.impl_get_master_slaves(): for slave in self.opt.impl_get_master_slaves():
values = self.context().cfgimpl_get_values() values = context.cfgimpl_get_values()
if not values.is_default_owner(slave): if not values.is_default_owner(slave):
#get multi without valid properties #get multi without valid properties
values.getitem(slave, values.getitem(slave, validate=False,
validate_properties=False validate_properties=False
).pop(index, force=True) ).pop(index, force=True)
#set value without valid properties #set value without valid properties
ret = super(Multi, self).pop(index) ret = super(Multi, self).pop(index)
self.context().cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force) context.cfgimpl_get_values()._setvalue(self.opt, self.path, self, validate_properties=not force)
return ret return ret

View File

@ -1,63 +1,61 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: Tiramisu\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-09-28 19:06+CEST\n" "POT-Creation-Date: 2014-03-12 21:49+CET\n"
"PO-Revision-Date: \n" "PO-Revision-Date: \n"
"Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n" "Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: Tiramisu's team <egarette@cadoles.com>\n"
"Language: fr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.4\n" "X-Generator: Poedit 1.5.4\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
#: tiramisu/autolib.py:144 #: tiramisu/autolib.py:162
msgid "" msgid ""
"unable to carry out a calculation, option {0} has properties: {1} for: {2}" "unable to carry out a calculation, option {0} has properties: {1} for: {2}"
msgstr "" msgstr ""
"impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : " "impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : "
"{2}" "{2}"
#: tiramisu/autolib.py:153 #: tiramisu/config.py:52
msgid ""
"unable to carry out a calculation, option value with multi types must have "
"same length for: {0}"
msgstr ""
"impossible d'effectuer le calcul, la valeur d'une option avec le type multi "
"doit avoir la même longueur pour : {0}"
#: tiramisu/config.py:51
msgid "descr must be an optiondescription, not {0}" msgid "descr must be an optiondescription, not {0}"
msgstr "descr doit être une optiondescription pas un {0}" msgstr "descr doit être une optiondescription pas un {0}"
#: tiramisu/config.py:126 #: tiramisu/config.py:127
msgid "unknown group_type: {0}" msgid "unknown group_type: {0}"
msgstr "group_type inconnu: {0}" msgstr "group_type inconnu: {0}"
#: tiramisu/config.py:162 #: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57
msgid "" #: tiramisu/value.py:485
"no option description found for this config (may be metaconfig without meta)" msgid "the context does not exist anymore"
msgstr "" msgstr "le context n'existe plus"
"pas d'option description trouvé pour cette config (peut être une metaconfig "
"sans meta)"
#: tiramisu/config.py:188 #: tiramisu/config.py:169
msgid "no option description found for this config (may be GroupConfig)"
msgstr ""
"pas d'option description trouvé pour cette config (peut être un GroupConfig)"
#: tiramisu/config.py:195
msgid "can't assign to an OptionDescription" msgid "can't assign to an OptionDescription"
msgstr "ne peut pas attribuer une valeur à une OptionDescription" msgstr "ne peut pas attribuer une valeur à une OptionDescription"
#: tiramisu/config.py:319 #: tiramisu/config.py:325
msgid "unknown type_ type {0}for _find" msgid "unknown type_ type {0}for _find"
msgstr "type_ type {0} pour _find inconnu" msgstr "type_ type {0} pour _find inconnu"
#: tiramisu/config.py:358 #: tiramisu/config.py:364
msgid "no option found in config with these criteria" msgid "no option found in config with these criteria"
msgstr "aucune option trouvée dans la config avec ces critères" msgstr "aucune option trouvée dans la config avec ces critères"
#: tiramisu/config.py:408 #: tiramisu/config.py:414
msgid "make_dict can't filtering with value without option" msgid "make_dict can't filtering with value without option"
msgstr "make_dict ne peut filtrer sur une valeur mais sans option" msgstr "make_dict ne peut filtrer sur une valeur mais sans option"
#: tiramisu/config.py:429 #: tiramisu/config.py:435
msgid "unexpected path {0}, should start with {1}" msgid "unexpected path {0}, should start with {1}"
msgstr "chemin imprévu {0}, devrait commencer par {1}" msgstr "chemin imprévu {0}, devrait commencer par {1}"
@ -65,41 +63,66 @@ msgstr "chemin imprévu {0}, devrait commencer par {1}"
msgid "opt in getowner must be an option not {0}" msgid "opt in getowner must be an option not {0}"
msgstr "opt dans getowner doit être une option pas {0}" msgstr "opt dans getowner doit être une option pas {0}"
#: tiramisu/option.py:68 #: tiramisu/config.py:532
msgid "cannot serialize Config with MetaConfig"
msgstr "impossible de sérialiser une Config avec une MetaConfig"
#: tiramisu/config.py:546
msgid "this storage is not serialisable, could be a none persistent storage"
msgstr "ce storage n'est sérialisable, devrait être une storage non persistant"
#: tiramisu/config.py:609
msgid "metaconfig's children must be a list"
msgstr "enfants d'une metaconfig doit être une liste"
#: tiramisu/config.py:703
msgid "metaconfig's children should be config, not {0}"
msgstr "enfants d'une metaconfig doit être une config, pas {0}"
#: tiramisu/config.py:707
msgid "child has already a metaconfig's"
msgstr "enfant a déjà une metaconfig"
#: tiramisu/config.py:711
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
"toutes les configs d'une metaconfig doivent avoir la même optiondescription"
#: tiramisu/option.py:67
msgid "invalid name: {0} for option" msgid "invalid name: {0} for option"
msgstr "nom invalide : {0} pour l'option" msgstr "nom invalide : {0} pour l'option"
#: tiramisu/option.py:77 #: tiramisu/option.py:76
msgid "invalid properties type {0} for {1}, must be a tuple" msgid "invalid properties type {0} for {1}, must be a tuple"
msgstr "type des properties invalide {0} pour {1}, doit être un tuple" msgstr "type des properties invalide {0} pour {1}, doit être un tuple"
#: tiramisu/option.py:115 #: tiramisu/option.py:114
msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule" msgstr "l'attribut {2} de l'objet '{0}' ({1}) est en lecture seule"
#: tiramisu/option.py:142 tiramisu/value.py:360 #: tiramisu/option.py:141 tiramisu/value.py:395
msgid "information's item not found: {0}" msgid "information's item not found: {0}"
msgstr "aucune config spécifié alors que c'est nécessaire" msgstr "aucune config spécifiée alors que c'est nécessaire"
#: tiramisu/option.py:204 #: tiramisu/option.py:203
msgid "cannot serialize Option, only in OptionDescription" msgid "cannot serialize Option, only in OptionDescription"
msgstr "ne peut serialiser une Option, seulement via une OptionDescription" msgstr "ne peut serialiser une Option, seulement via une OptionDescription"
#: tiramisu/option.py:307 #: tiramisu/option.py:306
msgid "a default_multi is set whereas multi is False in option: {0}" msgid "a default_multi is set whereas multi is False in option: {0}"
msgstr "" msgstr ""
"une default_multi est renseignée alors que multi est False dans l'option : " "une default_multi est renseignée alors que multi est False dans l'option : "
"{0}" "{0}"
#: tiramisu/option.py:313 #: tiramisu/option.py:312
msgid "invalid default_multi value {0} for option {1}: {2}" msgid "invalid default_multi value {0} for option {1}: {2}"
msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}" msgstr "la valeur default_multi est invalide {0} pour l'option {1} : {2}"
#: tiramisu/option.py:318 #: tiramisu/option.py:317
msgid "default value not allowed if option: {0} is calculated" msgid "default value not allowed if option: {0} is calculated"
msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculée" msgstr "la valeur par défaut n'est pas possible si l'option {0} est calculée"
#: tiramisu/option.py:321 #: tiramisu/option.py:320
msgid "" msgid ""
"params defined for a callback function but no callback defined yet for " "params defined for a callback function but no callback defined yet for "
"option {0}" "option {0}"
@ -107,221 +130,289 @@ msgstr ""
"params définis pour une fonction callback mais par de callback encore " "params définis pour une fonction callback mais par de callback encore "
"définis pour l'option {0}" "définis pour l'option {0}"
#: tiramisu/option.py:360 #: tiramisu/option.py:425 tiramisu/option.py:450
msgid "option not in all_cons_opts" msgid "invalid value for option {0}: {1}"
msgstr "option non présentante dans all_cons_opts" msgstr "valeur invalide pour l'option {0} : {1}"
#: tiramisu/option.py:432 tiramisu/value.py:545 #: tiramisu/option.py:444
msgid "invalid value {0} for option {1}: {2}" msgid "warning on the value of the option {0}: {1}"
msgstr "valeur invalide {0} pour l'option {1} : {2}" msgstr "avertissement sur la valeur de l'option {0} : {1}"
#: tiramisu/option.py:449 #: tiramisu/option.py:461
msgid "which must be a list" msgid "invalid value {0} for option {1} which must be a list"
msgstr "lequel doit être une liste" msgstr "valeur invalide pour l'option {0} : {1} laquelle doit être une liste"
#: tiramisu/option.py:509 #: tiramisu/option.py:519
msgid "consistency should be set with an option" msgid "consistency must be set with an option"
msgstr "consistency doit être configuré avec une option" msgstr "consistency doit être configuré avec une option"
#: tiramisu/option.py:511 #: tiramisu/option.py:521
msgid "cannot add consistency with itself" msgid "cannot add consistency with itself"
msgstr "ne peut ajouter une consistency avec lui même" msgstr "ne peut ajouter une consistency avec lui même"
#: tiramisu/option.py:513 #: tiramisu/option.py:523
msgid "every options in consistency should be multi or none" msgid "every options in consistency must be multi or none"
msgstr "" msgstr ""
"toutes les options d'une consistency devrait être multi ou ne pas l'être" "toutes les options d'une consistency doivent être multi ou ne pas l'être"
#: tiramisu/option.py:533 #: tiramisu/option.py:544
msgid "same value for {0} and {1}" msgid "same value for {0} and {1}, should be different"
msgstr "même valeur pour {0} et {1}" msgstr "même valeur pour {0} et {1}, devrait être différent"
#: tiramisu/option.py:642 #: tiramisu/option.py:546
msgid "same value for {0} and {1}, must be different"
msgstr "même valeur pour {0} et {1}, doit être différent"
#: tiramisu/option.py:640
msgid "values must be a tuple for {0}" msgid "values must be a tuple for {0}"
msgstr "values doit être un tuple pour {0}" msgstr "values doit être un tuple pour {0}"
#: tiramisu/option.py:645 #: tiramisu/option.py:643
msgid "open_values must be a boolean for {0}" msgid "open_values must be a boolean for {0}"
msgstr "open_values doit être un booléen pour {0}" msgstr "open_values doit être un booléen pour {0}"
#: tiramisu/option.py:667 #: tiramisu/option.py:665
msgid "value {0} is not permitted, only {1} is allowed" msgid "value {0} is not permitted, only {1} is allowed"
msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées" msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées"
#: tiramisu/option.py:679 #: tiramisu/option.py:677
msgid "value must be a boolean" msgid "invalid boolean"
msgstr "valeur doit être un booléen" msgstr "booléen invalide"
#: tiramisu/option.py:689 #: tiramisu/option.py:687
msgid "value must be an integer" msgid "invalid integer"
msgstr "valeur doit être un nombre entier" msgstr "nombre invalide"
#: tiramisu/option.py:699 #: tiramisu/option.py:697
msgid "value must be a float" msgid "invalid float"
msgstr "valeur doit être un nombre flottant" msgstr "invalide nombre flottan"
#: tiramisu/option.py:709 #: tiramisu/option.py:707
msgid "value must be a string, not {0}" msgid "invalid string"
msgstr "valeur doit être une chaîne, pas {0}" msgstr "invalide caractère"
#: tiramisu/option.py:727 #: tiramisu/option.py:724
msgid "value must be an unicode" msgid "invalid unicode"
msgstr "valeur doit être une valeur unicode" msgstr "invalide unicode"
#: tiramisu/option.py:739 #: tiramisu/option.py:736
msgid "malformed symlinkoption must be an option for symlink {0}" msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr "symlinkoption mal formé, doit être une option pour symlink {0}" msgstr "symlinkoption mal formé, doit être une option pour symlink {0}"
#: tiramisu/option.py:788 #: tiramisu/option.py:787 tiramisu/option.py:790 tiramisu/option.py:795
msgid "invalid IP {0}" msgid "invalid IP"
msgstr "adresse IP invalide {0}" msgstr "adresse IP invalide"
#: tiramisu/option.py:793 #: tiramisu/option.py:801
msgid "IP mustn't not be in reserved class" msgid "IP shouldn't be in reserved class"
msgstr "IP ne doit pas être d'une classe reservée" msgstr "l'adresse IP ne devrait pas être d'une classe réservée"
#: tiramisu/option.py:795 #: tiramisu/option.py:803
msgid "IP must be in private class" msgid "invalid IP, mustn't be in reserved class"
msgstr "IP doit être dans la classe privée" msgstr "adresse IP invalide, ne doit pas être dans une classe réservée"
#: tiramisu/option.py:833 #: tiramisu/option.py:807
msgid "inconsistency in allowed range" msgid "IP should be in private class"
msgstr "inconsistence dans la plage autorisée" msgstr "l'adresse IP devrait être dans une classe privée"
#: tiramisu/option.py:838 #: tiramisu/option.py:809
msgid "max value is empty" msgid "invalid IP, must be in private class"
msgstr "la valeur maximum est vide" msgstr "adresse IP invalide, doit être dans la classe privée"
#: tiramisu/option.py:877 #: tiramisu/option.py:814 tiramisu/option.py:989
msgid "invalid network address {0}"
msgstr "adresse réseau invalide {0}"
#: tiramisu/option.py:882
msgid "network shall not be in reserved class"
msgstr "le réseau ne doit pas être dans la classe reservée"
#: tiramisu/option.py:894
msgid "invalid netmask address {0}"
msgstr "masque de sous-réseau invalide {0}"
#: tiramisu/option.py:910
msgid "invalid len for opts"
msgstr "longueur invalide pour opts"
#: tiramisu/option.py:922
msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP"
msgstr "réseau invalide {0} ({1}) avec masque {2} ({3}), ce réseau est une IP"
#: tiramisu/option.py:927
msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network"
msgstr "IP invalide {0} ({1}) avec masque {2} ({3}), cette IP est un réseau"
#: tiramisu/option.py:932
msgid "invalid IP {0} ({1}) with netmask {2} ({3})"
msgstr "IP invalide {0} ({1}) avec masque {2} ({3})"
#: tiramisu/option.py:934
msgid "invalid network {0} ({1}) with netmask {2} ({3})"
msgstr "réseau invalide {0} ({1}) avec masque {2} ({3})"
#: tiramisu/option.py:948
msgid "invalid broadcast address {0}"
msgstr "adresse de broadcast invalide {0}"
#: tiramisu/option.py:952
msgid "invalid len for vals" msgid "invalid len for vals"
msgstr "longueur invalide pour vals" msgstr "longueur invalide pour vals"
#: tiramisu/option.py:957 #: tiramisu/option.py:820
msgid "IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})"
msgstr "IP {0} ({1}) pas dans le réseau {2} ({3}) avec le masque {4} ({5})"
#: tiramisu/option.py:823
msgid "invalid IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})"
msgstr ""
"IP invalide {0} ({1}) pas dans le réseau {2} ({3}) avec le masque {4} ({5})"
#: tiramisu/option.py:864
msgid "inconsistency in allowed range"
msgstr "inconsistence dans la plage autorisée"
#: tiramisu/option.py:869
msgid "max value is empty"
msgstr "la valeur maximum est vide"
#: tiramisu/option.py:886
msgid "invalid port, range must have two values only"
msgstr "port invalide, une plage doit avoir deux valeurs seulement"
#: tiramisu/option.py:889
msgid "invalid port, first port in range must be smaller than the second one"
msgstr ""
"port invalide, le premier port d'une plage doit être plus petit que le second"
#: tiramisu/option.py:898
msgid "invalid port"
msgstr "port invalide"
#: tiramisu/option.py:900
msgid "invalid port, must be an between {0} and {1}"
msgstr "port invalide, port doit être entre {0} et {1}"
#: tiramisu/option.py:914
msgid "invalid network address"
msgstr "adresse réseau invalide"
#: tiramisu/option.py:920
msgid "network address shouldn't be in reserved class"
msgstr "l'adresse réseau ne devait pas être dans la classe réservée"
#: tiramisu/option.py:922
msgid "invalid network address, mustn't be in reserved class"
msgstr "adresse réseau invalide, ne doit pas être dans la classe réservée"
#: tiramisu/option.py:935
msgid "invalid netmask address"
msgstr "masque de sous-réseau invalide"
#: tiramisu/option.py:952
msgid "invalid len for opts"
msgstr "longueur invalide pour opts"
#: tiramisu/option.py:966
msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network"
msgstr "IP invalide {0} ({1}) avec masque {2}, cette IP est un réseau"
#: tiramisu/option.py:971
msgid "invalid network {0} ({1}) with netmask {2}"
msgstr "réseau invalide {0} ({1}) avec masque {2}"
#: tiramisu/option.py:985
msgid "invalid broadcast address"
msgstr "adresse de broadcast invalide"
#: tiramisu/option.py:994
msgid "" msgid ""
"invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})"
msgstr "" msgstr ""
"Broadcast invalide {0} ({1}) avec le réseau {2} ({3}) et le masque {4} ({5})" "Broadcast invalide {0} ({1}) avec le réseau {2} ({3}) et le masque {4} ({5})"
#: tiramisu/option.py:979 #: tiramisu/option.py:1016
msgid "unknown type_ {0} for hostname" msgid "unknown type_ {0} for hostname"
msgstr "type_ inconnu {0} pour le nom d'hôte" msgstr "type_ inconnu {0} pour le nom d'hôte"
#: tiramisu/option.py:982 #: tiramisu/option.py:1019
msgid "allow_ip must be a boolean" msgid "allow_ip must be a boolean"
msgstr "allow_ip doit être un booléen" msgstr "allow_ip doit être un booléen"
#: tiramisu/option.py:1012 #: tiramisu/option.py:1021
msgid "invalid value for {0}, must have dot" msgid "allow_without_dot must be a boolean"
msgstr "valeur invalide pour {0}, doit avoir un point" msgstr "allow_without_dot doit être un booléen"
#: tiramisu/option.py:1015 #: tiramisu/option.py:1065
msgid "invalid domainname's length for {0} (max {1})" msgid "invalid domainname, must have dot"
msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})" msgstr "nom de domaine invalide, doit avoir un point"
#: tiramisu/option.py:1018 #: tiramisu/option.py:1067
msgid "invalid domainname's length for {0} (min 2)" msgid "invalid domainname's length (max 255)"
msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)" msgstr "longueur du nom de domaine invalide (maximum {1})"
#: tiramisu/option.py:1022 #: tiramisu/option.py:1069
msgid "invalid domainname's length (min 2)"
msgstr "longueur du nom de domaine invalide (minimum 2)"
#: tiramisu/option.py:1071
msgid "invalid domainname" msgid "invalid domainname"
msgstr "nom de domaine invalide" msgstr "nom de domaine invalide"
#: tiramisu/option.py:1049 #: tiramisu/option.py:1084
msgid "invalid email address, must contains one @"
msgstr "adresse email invalide, doit contenir un @"
#: tiramisu/option.py:1087
msgid "invalid username in email address"
msgstr "nom d'utilisateur invalide dans une adresse email"
#: tiramisu/option.py:1100
msgid "invalid url, must start with http:// or https://"
msgstr "URL invalide, doit démarrer avec http:// ou https://"
#: tiramisu/option.py:1119
msgid "invalid url, port must be an between 0 and 65536"
msgstr "URL invalide, port doit être entre 0 et 65536"
#: tiramisu/option.py:1125
msgid "invalid url, must ends with filename"
msgstr "URL invalide, doit finir avec un nom de fichier"
#: tiramisu/option.py:1137
msgid "invalid username"
msgstr "utilisateur invalide"
#: tiramisu/option.py:1148
msgid "invalid filename"
msgstr "nom de fichier invalide"
#: tiramisu/option.py:1175
msgid "duplicate option name: {0}" msgid "duplicate option name: {0}"
msgstr "nom de l'option dupliqué : {0}" msgstr "nom de l'option dupliqué : {0}"
#: tiramisu/option.py:1067 #: tiramisu/option.py:1193
msgid "unknown Option {0} in OptionDescription {1}" msgid "unknown Option {0} in OptionDescription {1}"
msgstr "Option {0} inconnue pour l'OptionDescription {1}" msgstr "Option {0} inconnue pour l'OptionDescription {1}"
#: tiramisu/option.py:1118 #: tiramisu/option.py:1244
msgid "duplicate option: {0}" msgid "duplicate option: {0}"
msgstr "option dupliquée : {0}" msgstr "option dupliquée : {0}"
#: tiramisu/option.py:1148 #: tiramisu/option.py:1275
msgid "consistency with option {0} which is not in Config" msgid "consistency with option {0} which is not in Config"
msgstr "consistency avec l'option {0} qui n'est pas dans une Config" msgstr "consistency avec l'option {0} qui n'est pas dans une Config"
#: tiramisu/option.py:1156 #: tiramisu/option.py:1283
msgid "no option for path {0}" msgid "no option for path {0}"
msgstr "pas d'option pour le chemin {0}" msgstr "pas d'option pour le chemin {0}"
#: tiramisu/option.py:1162 #: tiramisu/option.py:1289
msgid "no option {0} found" msgid "no option {0} found"
msgstr "pas d'option {0} trouvée" msgstr "pas d'option {0} trouvée"
#: tiramisu/option.py:1172 #: tiramisu/option.py:1299
msgid "cannot change group_type if already set (old {0}, new {1})" msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})" msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1})"
#: tiramisu/option.py:1185 #: tiramisu/option.py:1311
msgid "master group {0} shall not have a subgroup" msgid "master group {0} shall not have a subgroup"
msgstr "groupe maître {0} ne doit pas avoir de sous-groupe" msgstr "groupe maître {0} ne doit pas avoir de sous-groupe"
#: tiramisu/option.py:1188 #: tiramisu/option.py:1314
msgid "master group {0} shall not have a symlinkoption" msgid "master group {0} shall not have a symlinkoption"
msgstr "groupe maître {0} ne doit pas avoir de symlinkoption" msgstr "groupe maître {0} ne doit pas avoir de symlinkoption"
#: tiramisu/option.py:1191 #: tiramisu/option.py:1317
msgid "not allowed option {0} in group {1}: this option is not a multi" msgid "not allowed option {0} in group {1}: this option is not a multi"
msgstr "" msgstr ""
"option non autorisée {0} dans le groupe {1} : cette option n'est pas une " "option non autorisée {0} dans le groupe {1} : cette option n'est pas une "
"multi" "multi"
#: tiramisu/option.py:1202 #: tiramisu/option.py:1327
msgid "master group with wrong master name for {0}" msgid "master group with wrong master name for {0}"
msgstr "le groupe maître avec un nom de maître érroné pour {0}" msgstr "le groupe maître avec un nom de maître érroné pour {0}"
#: tiramisu/option.py:1211 #: tiramisu/option.py:1335
msgid "no child has same nom has master group for: {0}" msgid "callback of master's option shall not refered a slave's ones"
msgstr "pas d'enfant avec le nom du groupe maître pour {0} " msgstr ""
"callback d'une variable maitre ne devrait pas référencer des variables "
"esclaves"
#: tiramisu/option.py:1214 #: tiramisu/option.py:1343
msgid "group_type: {0} not allowed" msgid "group_type: {0} not allowed"
msgstr "group_type : {0} non autorisé" msgstr "group_type : {0} non autorisé"
#: tiramisu/option.py:1306 #: tiramisu/option.py:1443
msgid "malformed requirements type for option: {0}, must be a dict" msgid "malformed requirements type for option: {0}, must be a dict"
msgstr "" msgstr ""
"type requirements malformé pour l'option : {0}, doit être un dictionnaire" "type requirements malformé pour l'option : {0}, doit être un dictionnaire"
#: tiramisu/option.py:1323 #: tiramisu/option.py:1460
msgid "" msgid ""
"malformed requirements for option: {0} require must have option, expected " "malformed requirements for option: {0} require must have option, expected "
"and action keys" "and action keys"
@ -329,110 +420,110 @@ msgstr ""
"requirements malformé pour l'option : {0} l'exigence doit avoir les clefs " "requirements malformé pour l'option : {0} l'exigence doit avoir les clefs "
"option, expected et action" "option, expected et action"
#: tiramisu/option.py:1328 #: tiramisu/option.py:1465
msgid "malformed requirements for option: {0} inverse must be boolean" msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr "" msgstr ""
"requirements mal formés pour l'option : {0} inverse doit être un booléen" "requirements mal formés pour l'option : {0} inverse doit être un booléen"
#: tiramisu/option.py:1332 #: tiramisu/option.py:1469
msgid "malformed requirements for option: {0} transitive must be boolean" msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr "" msgstr ""
"requirements mal formés pour l'option : {0} transitive doit être booléen" "requirements mal formés pour l'option : {0} transitive doit être booléen"
#: tiramisu/option.py:1336 #: tiramisu/option.py:1473
msgid "malformed requirements for option: {0} same_action must be boolean" msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr "" msgstr ""
"requirements mal formés pour l'option : {0} same_action doit être un booléen" "requirements mal formés pour l'option : {0} same_action doit être un booléen"
#: tiramisu/option.py:1340 #: tiramisu/option.py:1477
msgid "malformed requirements must be an option in option {0}" msgid "malformed requirements must be an option in option {0}"
msgstr "requirements mal formés doit être une option dans l'option {0}" msgstr "requirements mal formés doit être une option dans l'option {0}"
#: tiramisu/option.py:1343 #: tiramisu/option.py:1480
msgid "malformed requirements option {0} should not be a multi" msgid "malformed requirements option {0} must not be a multi"
msgstr "requirements mal formés l'option {0} ne doit pas être une multi" msgstr "requirements mal formés l'option {0} ne doit pas être une multi"
#: tiramisu/option.py:1349 #: tiramisu/option.py:1486
msgid "" msgid ""
"malformed requirements second argument must be valid for option {0}: {1}" "malformed requirements second argument must be valid for option {0}: {1}"
msgstr "" msgstr ""
"requirements mal formés deuxième argument doit être valide pour l'option " "requirements mal formés deuxième argument doit être valide pour l'option "
"{0} : {1}" "{0} : {1}"
#: tiramisu/option.py:1354 #: tiramisu/option.py:1491
msgid "inconsistency in action types for option: {0} action: {1}" msgid "inconsistency in action types for option: {0} action: {1}"
msgstr "incohérence dans les types action pour l'option : {0} action {1}" msgstr "incohérence dans les types action pour l'option : {0} action {1}"
#: tiramisu/option.py:1379 #: tiramisu/option.py:1516
msgid "{0} should be a function" msgid "{0} must be a function"
msgstr "{0} doit être une fonction" msgstr "{0} doit être une fonction"
#: tiramisu/option.py:1382 #: tiramisu/option.py:1519
msgid "{0}_params should be a dict" msgid "{0}_params must be a dict"
msgstr "{0}_params devrait être un dict" msgstr "{0}_params doit être un dict"
#: tiramisu/option.py:1385 #: tiramisu/option.py:1522
msgid "{0}_params with key {1} should not have length different to 1" msgid "{0}_params with key {1} mustn't have length different to 1"
msgstr "" msgstr ""
"{0}_params avec la clef {1} devrait ne pas avoir une longueur différent de 1" "{0}_params avec la clef {1} ne doit pas avoir une longueur différent de 1"
#: tiramisu/option.py:1389 #: tiramisu/option.py:1526
msgid "{0}_params should be tuple for key \"{1}\"" msgid "{0}_params must be tuple for key \"{1}\""
msgstr "{0}_params devrait être un tuple pour la clef \"{1}\"" msgstr "{0}_params doit être un tuple pour la clef \"{1}\""
#: tiramisu/option.py:1395 #: tiramisu/option.py:1532
msgid "validator not support tuple" msgid "validator not support tuple"
msgstr "validator n'accepte pas de tuple" msgstr "validator n'accepte pas de tuple"
#: tiramisu/option.py:1398 #: tiramisu/option.py:1535
msgid "{0}_params should have an option not a {0} for first argument" msgid "{0}_params must have an option not a {0} for first argument"
msgstr "{0}_params devrait avoir une option pas un {0} pour premier argument" msgstr "{0}_params doit avoir une option pas un {0} pour premier argument"
#: tiramisu/option.py:1402 #: tiramisu/option.py:1539
msgid "{0}_params should have a boolean not a {0} for second argument" msgid "{0}_params must have a boolean not a {0} for second argument"
msgstr "{0}_params devrait avoir un boolean pas un {0} pour second argument" msgstr "{0}_params doit avoir un booléen pas un {0} pour second argument"
#: tiramisu/setting.py:111 #: tiramisu/setting.py:116
msgid "can't rebind {0}" msgid "can't rebind {0}"
msgstr "ne peut redéfinir ({0})" msgstr "ne peut redéfinir ({0})"
#: tiramisu/setting.py:116 #: tiramisu/setting.py:121
msgid "can't unbind {0}" msgid "can't unbind {0}"
msgstr "ne peut supprimer ({0})" msgstr "ne peut supprimer ({0})"
#: tiramisu/setting.py:254 #: tiramisu/setting.py:272
msgid "cannot append {0} property for option {1}: this property is calculated" msgid "cannot append {0} property for option {1}: this property is calculated"
msgstr "" msgstr ""
"ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est " "ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est "
"calculée" "calculée"
#: tiramisu/setting.py:317 #: tiramisu/setting.py:363
msgid "opt and all_properties must not be set together in reset" msgid "opt and all_properties must not be set together in reset"
msgstr "opt et all_properties ne doit pas être renseigné ensemble dans reset" msgstr "opt et all_properties ne doit pas être renseigné ensemble dans reset"
#: tiramisu/setting.py:332 #: tiramisu/setting.py:378
msgid "if opt is not None, path should not be None in _getproperties" msgid "if opt is not None, path should not be None in _getproperties"
msgstr "" msgstr ""
"si opt n'est pas None, path devrait ne pas être à None dans _getproperties" "si opt n'est pas None, path devrait ne pas être à None dans _getproperties"
#: tiramisu/setting.py:435 #: tiramisu/setting.py:487
msgid "cannot change the value for option {0} this option is frozen" msgid "cannot change the value for option {0} this option is frozen"
msgstr "" msgstr ""
"ne peut modifier la valeur de l'option {0} cette option n'est pas modifiable" "ne peut modifier la valeur de l'option {0} cette option n'est pas modifiable"
#: tiramisu/setting.py:441 #: tiramisu/setting.py:493
msgid "trying to access to an option named: {0} with properties {1}" msgid "trying to access to an option named: {0} with properties {1}"
msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1}" msgstr "tentative d'accès à une option nommée : {0} avec les propriétés {1}"
#: tiramisu/setting.py:459 #: tiramisu/setting.py:511
msgid "permissive must be a tuple" msgid "permissive must be a tuple"
msgstr "permissive doit être un tuple" msgstr "permissive doit être un tuple"
#: tiramisu/setting.py:466 tiramisu/value.py:299 #: tiramisu/setting.py:518 tiramisu/value.py:334
msgid "invalid generic owner {0}" msgid "invalid generic owner {0}"
msgstr "invalide owner générique {0}" msgstr "invalide owner générique {0}"
#: tiramisu/setting.py:553 #: tiramisu/setting.py:606
msgid "" msgid ""
"malformed requirements imbrication detected for option: '{0}' with " "malformed requirements imbrication detected for option: '{0}' with "
"requirement on: '{1}'" "requirement on: '{1}'"
@ -440,75 +531,119 @@ msgstr ""
"imbrication de requirements mal formés detectée pour l'option : '{0}' avec " "imbrication de requirements mal formés detectée pour l'option : '{0}' avec "
"requirement sur : '{1}'" "requirement sur : '{1}'"
#: tiramisu/setting.py:565 #: tiramisu/setting.py:617
msgid "option '{0}' has requirement's property error: {1} {2}" msgid "option '{0}' has requirement's property error: {1} {2}"
msgstr "l'option '{0}' a une erreur de propriété pour le requirement : {1} {2}" msgstr "l'option '{0}' a une erreur de propriété pour le requirement : {1} {2}"
#: tiramisu/storage/__init__.py:47 #: tiramisu/storage/__init__.py:52
msgid "storage_type is already set, cannot rebind it" msgid "storage_type is already set, cannot rebind it"
msgstr "storage_type est déjà défini, impossible de le redéfinir" msgstr "storage_type est déjà défini, impossible de le redéfinir"
#: tiramisu/storage/__init__.py:81 #: tiramisu/storage/__init__.py:86
msgid "option {0} not already exists in storage {1}" msgid "option {0} not already exists in storage {1}"
msgstr "option {0} n'existe pas dans l'espace de stockage {1}" msgstr "option {0} n'existe pas dans l'espace de stockage {1}"
#: tiramisu/storage/dictionary/storage.py:37 #: tiramisu/storage/dictionary/storage.py:39
msgid "dictionary storage cannot delete session" msgid "dictionary storage cannot delete session"
msgstr "" msgstr ""
"impossible de supprimer une session dans un espace de stockage dictionary" "impossible de supprimer une session dans un espace de stockage dictionary"
#: tiramisu/storage/dictionary/storage.py:48 #: tiramisu/storage/dictionary/storage.py:50
msgid "session already used" msgid "session already used"
msgstr "session déjà utilisée" msgstr "session déjà utilisée"
#: tiramisu/storage/dictionary/storage.py:50 #: tiramisu/storage/dictionary/storage.py:52
msgid "a dictionary cannot be persistent" msgid "a dictionary cannot be persistent"
msgstr "un espace de stockage dictionary ne peut être persistant" msgstr "un espace de stockage dictionary ne peut être persistant"
#: tiramisu/value.py:306 #: tiramisu/value.py:341
msgid "no value for {0} cannot change owner to {1}" msgid "no value for {0} cannot change owner to {1}"
msgstr "pas de valeur pour {0} ne peut changer d'utilisateur pour {1}" msgstr "pas de valeur pour {0} ne peut changer d'utilisateur pour {1}"
#: tiramisu/value.py:414 #: tiramisu/value.py:423
msgid "can force cache only if cache is actived in config"
msgstr ""
"peut force la mise en cache seulement si le cache est activé dans la config"
#: tiramisu/value.py:462
msgid "{0} is already a Multi "
msgstr "{0} est déjà une Multi"
#: tiramisu/value.py:498 tiramisu/value.py:562
msgid "invalid len for the slave: {0} which has {1} as master" msgid "invalid len for the slave: {0} which has {1} as master"
msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître" msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître"
#: tiramisu/value.py:438 #: tiramisu/value.py:534
msgid "invalid len for the master: {0} which has {1} as slave with greater len"
msgstr ""
"longueur invalide pour un maître : {0} qui a {1} une esclave avec une plus "
"grande longueur"
#: tiramisu/value.py:468
msgid "cannot append a value on a multi option {0} which is a slave" msgid "cannot append a value on a multi option {0} which is a slave"
msgstr "ne peut ajouter une valeur sur l'option multi {0} qui est une esclave" msgstr "ne peut ajouter une valeur sur l'option multi {0} qui est une esclave"
#: tiramisu/value.py:505 #: tiramisu/value.py:572
msgid "cannot sort multi option {0} if master or slave" msgid "cannot sort multi option {0} if master or slave"
msgstr "ne peut trier une option multi {0} pour une maître ou une esclave" msgstr "ne peut trier une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:509 #: tiramisu/value.py:576
msgid "cmp is not permitted in python v3 or greater" msgid "cmp is not permitted in python v3 or greater"
msgstr "cmp n'est pas permis en python v3 ou supérieure" msgstr "cmp n'est pas permis en python v3 ou supérieure"
#: tiramisu/value.py:518 #: tiramisu/value.py:585
msgid "cannot reverse multi option {0} if master or slave" msgid "cannot reverse multi option {0} if master or slave"
msgstr "ne peut inverser une option multi {0} pour une maître ou une esclave" msgstr "ne peut inverser une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:526 #: tiramisu/value.py:593
msgid "cannot insert multi option {0} if master or slave" msgid "cannot insert multi option {0} if master or slave"
msgstr "ne peut insérer une option multi {0} pour une maître ou une esclave" msgstr "ne peut insérer une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:534 #: tiramisu/value.py:601
msgid "cannot extend multi option {0} if master or slave" msgid "cannot extend multi option {0} if master or slave"
msgstr "ne peut étendre une option multi {0} pour une maître ou une esclave" msgstr "ne peut étendre une option multi {0} pour une maître ou une esclave"
#: tiramisu/value.py:562 #: tiramisu/value.py:612
msgid "invalid value {0} for option {1}: {2}"
msgstr "valeur invalide {0} pour l'option {1} : {2}"
#: tiramisu/value.py:630
msgid "cannot pop a value on a multi option {0} which is a slave" msgid "cannot pop a value on a multi option {0} which is a slave"
msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave" msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave"
#~ msgid "invalid value {0} for option {1} which must be a list" #~ msgid "option not in all_cons_opts"
#~ msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste" #~ msgstr "option non présentante dans all_cons_opts"
#~ msgid "invalid network {0} ({1}) with netmask {2}, this network is an IP"
#~ msgstr "réseau invalide {0} ({1}) avec masque {2}, ce réseau est une IP"
#~ msgid "invalid IP {0} ({1}) with netmask {2}"
#~ msgstr "IP invalide {0} ({1}) avec masque {2}"
#~ msgid ""
#~ "invalid len for the master: {0} which has {1} as slave with greater len"
#~ msgstr ""
#~ "longueur invalide pour un maître : {0} qui a {1} une esclave avec une "
#~ "plus grande longueur"
#~ msgid ""
#~ "unable to carry out a calculation, option value with multi types must "
#~ "have same length for: {0}"
#~ msgstr ""
#~ "impossible d'effectuer le calcul, la valeur d'une option avec le type "
#~ "multi doit avoir la même longueur pour : {0}"
#~ msgid "no child has same nom has master group for: {0}"
#~ msgstr "pas d'enfant avec le nom du groupe maître pour {0} "
#~ msgid "value must be a boolean"
#~ msgstr "valeur doit être un booléen"
#~ msgid "value must be an integer"
#~ msgstr "valeur doit être un nombre entier"
#~ msgid "value must be a float"
#~ msgstr "valeur doit être un nombre flottant"
#~ msgid "value must be a string, not {0}"
#~ msgstr "valeur doit être une chaîne, pas {0}"
#~ msgid "value must be an unicode"
#~ msgstr "valeur doit être une valeur unicode"
#~ msgid "invalid value {0} for option {1} must be different as {2} option" #~ msgid "invalid value {0} for option {1} must be different as {2} option"
#~ msgstr "" #~ msgstr ""
@ -532,17 +667,6 @@ msgstr "ne peut supprimer une valeur dans l'option multi {0} qui est esclave"
#~ msgid "invalid name: {0} for optiondescription" #~ msgid "invalid name: {0} for optiondescription"
#~ msgstr "nom invalide : {0} pour l'optiondescription" #~ msgstr "nom invalide : {0} pour l'optiondescription"
#~ msgid "metaconfig's children must be config, not {0}"
#~ msgstr "enfants d'une metaconfig doit être une config, pas {0}"
#~ msgid "all config in metaconfig must have same optiondescription"
#~ msgstr ""
#~ "toutes les configs d'une metaconfig doivent avoir la même "
#~ "optiondescription"
#~ msgid "child has already a metaconfig's"
#~ msgstr "enfant a déjà une metaconfig"
#~ msgid "not allowed group_type : {0}" #~ msgid "not allowed group_type : {0}"
#~ msgstr "group_type non autorisé : {0}" #~ msgstr "group_type non autorisé : {0}"

View File

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2013-09-28 19:06+CEST\n" "POT-Creation-Date: 2014-03-12 21:49+CET\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,43 +15,44 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
#: tiramisu/autolib.py:144 #: tiramisu/autolib.py:162
msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}" msgid "unable to carry out a calculation, option {0} has properties: {1} for: {2}"
msgstr "" msgstr ""
#: tiramisu/autolib.py:153 #: tiramisu/config.py:52
msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}"
msgstr ""
#: tiramisu/config.py:51
msgid "descr must be an optiondescription, not {0}" msgid "descr must be an optiondescription, not {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:126 #: tiramisu/config.py:127
msgid "unknown group_type: {0}" msgid "unknown group_type: {0}"
msgstr "" msgstr ""
#: tiramisu/config.py:162 #: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57
msgid "no option description found for this config (may be metaconfig without meta)" #: tiramisu/value.py:485
msgid "the context does not exist anymore"
msgstr "" msgstr ""
#: tiramisu/config.py:188 #: tiramisu/config.py:169
msgid "no option description found for this config (may be GroupConfig)"
msgstr ""
#: tiramisu/config.py:195
msgid "can't assign to an OptionDescription" msgid "can't assign to an OptionDescription"
msgstr "" msgstr ""
#: tiramisu/config.py:319 #: tiramisu/config.py:325
msgid "unknown type_ type {0}for _find" msgid "unknown type_ type {0}for _find"
msgstr "" msgstr ""
#: tiramisu/config.py:358 #: tiramisu/config.py:364
msgid "no option found in config with these criteria" msgid "no option found in config with these criteria"
msgstr "" msgstr ""
#: tiramisu/config.py:408 #: tiramisu/config.py:414
msgid "make_dict can't filtering with value without option" msgid "make_dict can't filtering with value without option"
msgstr "" msgstr ""
#: tiramisu/config.py:429 #: tiramisu/config.py:435
msgid "unexpected path {0}, should start with {1}" msgid "unexpected path {0}, should start with {1}"
msgstr "" msgstr ""
@ -59,411 +60,507 @@ msgstr ""
msgid "opt in getowner must be an option not {0}" msgid "opt in getowner must be an option not {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:68 #: tiramisu/config.py:532
msgid "cannot serialize Config with MetaConfig"
msgstr ""
#: tiramisu/config.py:546
msgid "this storage is not serialisable, could be a none persistent storage"
msgstr ""
#: tiramisu/config.py:609
msgid "metaconfig's children must be a list"
msgstr ""
#: tiramisu/config.py:703
msgid "metaconfig's children should be config, not {0}"
msgstr ""
#: tiramisu/config.py:707
msgid "child has already a metaconfig's"
msgstr ""
#: tiramisu/config.py:711
msgid "all config in metaconfig must have the same optiondescription"
msgstr ""
#: tiramisu/option.py:67
msgid "invalid name: {0} for option" msgid "invalid name: {0} for option"
msgstr "" msgstr ""
#: tiramisu/option.py:77 #: tiramisu/option.py:76
msgid "invalid properties type {0} for {1}, must be a tuple" msgid "invalid properties type {0} for {1}, must be a tuple"
msgstr "" msgstr ""
#: tiramisu/option.py:115 #: tiramisu/option.py:114
msgid "'{0}' ({1}) object attribute '{2}' is read-only" msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr "" msgstr ""
#: tiramisu/option.py:142 tiramisu/value.py:360 #: tiramisu/option.py:141 tiramisu/value.py:395
msgid "information's item not found: {0}" msgid "information's item not found: {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:204 #: tiramisu/option.py:203
msgid "cannot serialize Option, only in OptionDescription" msgid "cannot serialize Option, only in OptionDescription"
msgstr "" msgstr ""
#: tiramisu/option.py:307 #: tiramisu/option.py:306
msgid "a default_multi is set whereas multi is False in option: {0}" msgid "a default_multi is set whereas multi is False in option: {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:313 #: tiramisu/option.py:312
msgid "invalid default_multi value {0} for option {1}: {2}" msgid "invalid default_multi value {0} for option {1}: {2}"
msgstr "" msgstr ""
#: tiramisu/option.py:318 #: tiramisu/option.py:317
msgid "default value not allowed if option: {0} is calculated" msgid "default value not allowed if option: {0} is calculated"
msgstr "" msgstr ""
#: tiramisu/option.py:321 #: tiramisu/option.py:320
msgid "params defined for a callback function but no callback defined yet for option {0}" msgid "params defined for a callback function but no callback defined yet for option {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:360 #: tiramisu/option.py:425 tiramisu/option.py:450
msgid "option not in all_cons_opts" msgid "invalid value for option {0}: {1}"
msgstr "" msgstr ""
#: tiramisu/option.py:432 tiramisu/value.py:545 #: tiramisu/option.py:444
msgid "invalid value {0} for option {1}: {2}" msgid "warning on the value of the option {0}: {1}"
msgstr "" msgstr ""
#: tiramisu/option.py:449 #: tiramisu/option.py:461
msgid "which must be a list" msgid "invalid value {0} for option {1} which must be a list"
msgstr "" msgstr ""
#: tiramisu/option.py:509 #: tiramisu/option.py:519
msgid "consistency should be set with an option" msgid "consistency must be set with an option"
msgstr "" msgstr ""
#: tiramisu/option.py:511 #: tiramisu/option.py:521
msgid "cannot add consistency with itself" msgid "cannot add consistency with itself"
msgstr "" msgstr ""
#: tiramisu/option.py:513 #: tiramisu/option.py:523
msgid "every options in consistency should be multi or none" msgid "every options in consistency must be multi or none"
msgstr "" msgstr ""
#: tiramisu/option.py:533 #: tiramisu/option.py:544
msgid "same value for {0} and {1}" msgid "same value for {0} and {1}, should be different"
msgstr "" msgstr ""
#: tiramisu/option.py:642 #: tiramisu/option.py:546
msgid "same value for {0} and {1}, must be different"
msgstr ""
#: tiramisu/option.py:640
msgid "values must be a tuple for {0}" msgid "values must be a tuple for {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:645 #: tiramisu/option.py:643
msgid "open_values must be a boolean for {0}" msgid "open_values must be a boolean for {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:667 #: tiramisu/option.py:665
msgid "value {0} is not permitted, only {1} is allowed" msgid "value {0} is not permitted, only {1} is allowed"
msgstr "" msgstr ""
#: tiramisu/option.py:679 #: tiramisu/option.py:677
msgid "value must be a boolean" msgid "invalid boolean"
msgstr "" msgstr ""
#: tiramisu/option.py:689 #: tiramisu/option.py:687
msgid "value must be an integer" msgid "invalid integer"
msgstr "" msgstr ""
#: tiramisu/option.py:699 #: tiramisu/option.py:697
msgid "value must be a float" msgid "invalid float"
msgstr "" msgstr ""
#: tiramisu/option.py:709 #: tiramisu/option.py:707
msgid "value must be a string, not {0}" msgid "invalid string"
msgstr "" msgstr ""
#: tiramisu/option.py:727 #: tiramisu/option.py:724
msgid "value must be an unicode" msgid "invalid unicode"
msgstr "" msgstr ""
#: tiramisu/option.py:739 #: tiramisu/option.py:736
msgid "malformed symlinkoption must be an option for symlink {0}" msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:788 #: tiramisu/option.py:787 tiramisu/option.py:790 tiramisu/option.py:795
msgid "invalid IP {0}" msgid "invalid IP"
msgstr "" msgstr ""
#: tiramisu/option.py:793 #: tiramisu/option.py:801
msgid "IP mustn't not be in reserved class" msgid "IP shouldn't be in reserved class"
msgstr "" msgstr ""
#: tiramisu/option.py:795 #: tiramisu/option.py:803
msgid "IP must be in private class" msgid "invalid IP, mustn't be in reserved class"
msgstr "" msgstr ""
#: tiramisu/option.py:833 #: tiramisu/option.py:807
msgid "inconsistency in allowed range" msgid "IP should be in private class"
msgstr "" msgstr ""
#: tiramisu/option.py:838 #: tiramisu/option.py:809
msgid "max value is empty" msgid "invalid IP, must be in private class"
msgstr "" msgstr ""
#: tiramisu/option.py:877 #: tiramisu/option.py:814 tiramisu/option.py:989
msgid "invalid network address {0}"
msgstr ""
#: tiramisu/option.py:882
msgid "network shall not be in reserved class"
msgstr ""
#: tiramisu/option.py:894
msgid "invalid netmask address {0}"
msgstr ""
#: tiramisu/option.py:910
msgid "invalid len for opts"
msgstr ""
#: tiramisu/option.py:922
msgid "invalid network {0} ({1}) with netmask {2} ({3}), this network is an IP"
msgstr ""
#: tiramisu/option.py:927
msgid "invalid IP {0} ({1}) with netmask {2} ({3}), this IP is a network"
msgstr ""
#: tiramisu/option.py:932
msgid "invalid IP {0} ({1}) with netmask {2} ({3})"
msgstr ""
#: tiramisu/option.py:934
msgid "invalid network {0} ({1}) with netmask {2} ({3})"
msgstr ""
#: tiramisu/option.py:948
msgid "invalid broadcast address {0}"
msgstr ""
#: tiramisu/option.py:952
msgid "invalid len for vals" msgid "invalid len for vals"
msgstr "" msgstr ""
#: tiramisu/option.py:957 #: tiramisu/option.py:820
msgid "IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})"
msgstr ""
#: tiramisu/option.py:823
msgid "invalid IP {0} ({1}) not in network {2} ({3}) with netmask {4} ({5})"
msgstr ""
#: tiramisu/option.py:864
msgid "inconsistency in allowed range"
msgstr ""
#: tiramisu/option.py:869
msgid "max value is empty"
msgstr ""
#: tiramisu/option.py:886
msgid "invalid port, range must have two values only"
msgstr ""
#: tiramisu/option.py:889
msgid "invalid port, first port in range must be smaller than the second one"
msgstr ""
#: tiramisu/option.py:898
msgid "invalid port"
msgstr ""
#: tiramisu/option.py:900
msgid "invalid port, must be an between {0} and {1}"
msgstr ""
#: tiramisu/option.py:914
msgid "invalid network address"
msgstr ""
#: tiramisu/option.py:920
msgid "network address shouldn't be in reserved class"
msgstr ""
#: tiramisu/option.py:922
msgid "invalid network address, mustn't be in reserved class"
msgstr ""
#: tiramisu/option.py:935
msgid "invalid netmask address"
msgstr ""
#: tiramisu/option.py:952
msgid "invalid len for opts"
msgstr ""
#: tiramisu/option.py:966
msgid "invalid IP {0} ({1}) with netmask {2}, this IP is a network"
msgstr ""
#: tiramisu/option.py:971
msgid "invalid network {0} ({1}) with netmask {2}"
msgstr ""
#: tiramisu/option.py:985
msgid "invalid broadcast address"
msgstr ""
#: tiramisu/option.py:994
msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})" msgid "invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})"
msgstr "" msgstr ""
#: tiramisu/option.py:979 #: tiramisu/option.py:1016
msgid "unknown type_ {0} for hostname" msgid "unknown type_ {0} for hostname"
msgstr "" msgstr ""
#: tiramisu/option.py:982 #: tiramisu/option.py:1019
msgid "allow_ip must be a boolean" msgid "allow_ip must be a boolean"
msgstr "" msgstr ""
#: tiramisu/option.py:1012 #: tiramisu/option.py:1021
msgid "invalid value for {0}, must have dot" msgid "allow_without_dot must be a boolean"
msgstr "" msgstr ""
#: tiramisu/option.py:1015 #: tiramisu/option.py:1065
msgid "invalid domainname's length for {0} (max {1})" msgid "invalid domainname, must have dot"
msgstr ""
#: tiramisu/option.py:1018
msgid "invalid domainname's length for {0} (min 2)"
msgstr ""
#: tiramisu/option.py:1022
msgid "invalid domainname"
msgstr ""
#: tiramisu/option.py:1049
msgid "duplicate option name: {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:1067 #: tiramisu/option.py:1067
msgid "unknown Option {0} in OptionDescription {1}" msgid "invalid domainname's length (max 255)"
msgstr "" msgstr ""
#: tiramisu/option.py:1118 #: tiramisu/option.py:1069
msgid "duplicate option: {0}" msgid "invalid domainname's length (min 2)"
msgstr ""
#: tiramisu/option.py:1071
msgid "invalid domainname"
msgstr ""
#: tiramisu/option.py:1084
msgid "invalid email address, must contains one @"
msgstr ""
#: tiramisu/option.py:1087
msgid "invalid username in email address"
msgstr ""
#: tiramisu/option.py:1100
msgid "invalid url, must start with http:// or https://"
msgstr ""
#: tiramisu/option.py:1119
msgid "invalid url, port must be an between 0 and 65536"
msgstr ""
#: tiramisu/option.py:1125
msgid "invalid url, must ends with filename"
msgstr ""
#: tiramisu/option.py:1137
msgid "invalid username"
msgstr "" msgstr ""
#: tiramisu/option.py:1148 #: tiramisu/option.py:1148
msgid "invalid filename"
msgstr ""
#: tiramisu/option.py:1175
msgid "duplicate option name: {0}"
msgstr ""
#: tiramisu/option.py:1193
msgid "unknown Option {0} in OptionDescription {1}"
msgstr ""
#: tiramisu/option.py:1244
msgid "duplicate option: {0}"
msgstr ""
#: tiramisu/option.py:1275
msgid "consistency with option {0} which is not in Config" msgid "consistency with option {0} which is not in Config"
msgstr "" msgstr ""
#: tiramisu/option.py:1156 #: tiramisu/option.py:1283
msgid "no option for path {0}" msgid "no option for path {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:1162 #: tiramisu/option.py:1289
msgid "no option {0} found" msgid "no option {0} found"
msgstr "" msgstr ""
#: tiramisu/option.py:1172 #: tiramisu/option.py:1299
msgid "cannot change group_type if already set (old {0}, new {1})" msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr "" msgstr ""
#: tiramisu/option.py:1185 #: tiramisu/option.py:1311
msgid "master group {0} shall not have a subgroup" msgid "master group {0} shall not have a subgroup"
msgstr "" msgstr ""
#: tiramisu/option.py:1188 #: tiramisu/option.py:1314
msgid "master group {0} shall not have a symlinkoption" msgid "master group {0} shall not have a symlinkoption"
msgstr "" msgstr ""
#: tiramisu/option.py:1191 #: tiramisu/option.py:1317
msgid "not allowed option {0} in group {1}: this option is not a multi" msgid "not allowed option {0} in group {1}: this option is not a multi"
msgstr "" msgstr ""
#: tiramisu/option.py:1202 #: tiramisu/option.py:1327
msgid "master group with wrong master name for {0}" msgid "master group with wrong master name for {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:1211 #: tiramisu/option.py:1335
msgid "no child has same nom has master group for: {0}" msgid "callback of master's option shall not refered a slave's ones"
msgstr ""
#: tiramisu/option.py:1214
msgid "group_type: {0} not allowed"
msgstr ""
#: tiramisu/option.py:1306
msgid "malformed requirements type for option: {0}, must be a dict"
msgstr ""
#: tiramisu/option.py:1323
msgid "malformed requirements for option: {0} require must have option, expected and action keys"
msgstr ""
#: tiramisu/option.py:1328
msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr ""
#: tiramisu/option.py:1332
msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr ""
#: tiramisu/option.py:1336
msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr ""
#: tiramisu/option.py:1340
msgid "malformed requirements must be an option in option {0}"
msgstr "" msgstr ""
#: tiramisu/option.py:1343 #: tiramisu/option.py:1343
msgid "malformed requirements option {0} should not be a multi" msgid "group_type: {0} not allowed"
msgstr "" msgstr ""
#: tiramisu/option.py:1349 #: tiramisu/option.py:1443
msgid "malformed requirements type for option: {0}, must be a dict"
msgstr ""
#: tiramisu/option.py:1460
msgid "malformed requirements for option: {0} require must have option, expected and action keys"
msgstr ""
#: tiramisu/option.py:1465
msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr ""
#: tiramisu/option.py:1469
msgid "malformed requirements for option: {0} transitive must be boolean"
msgstr ""
#: tiramisu/option.py:1473
msgid "malformed requirements for option: {0} same_action must be boolean"
msgstr ""
#: tiramisu/option.py:1477
msgid "malformed requirements must be an option in option {0}"
msgstr ""
#: tiramisu/option.py:1480
msgid "malformed requirements option {0} must not be a multi"
msgstr ""
#: tiramisu/option.py:1486
msgid "malformed requirements second argument must be valid for option {0}: {1}" msgid "malformed requirements second argument must be valid for option {0}: {1}"
msgstr "" msgstr ""
#: tiramisu/option.py:1354 #: tiramisu/option.py:1491
msgid "inconsistency in action types for option: {0} action: {1}" msgid "inconsistency in action types for option: {0} action: {1}"
msgstr "" msgstr ""
#: tiramisu/option.py:1379 #: tiramisu/option.py:1516
msgid "{0} should be a function" msgid "{0} must be a function"
msgstr "" msgstr ""
#: tiramisu/option.py:1382 #: tiramisu/option.py:1519
msgid "{0}_params should be a dict" msgid "{0}_params must be a dict"
msgstr "" msgstr ""
#: tiramisu/option.py:1385 #: tiramisu/option.py:1522
msgid "{0}_params with key {1} should not have length different to 1" msgid "{0}_params with key {1} mustn't have length different to 1"
msgstr "" msgstr ""
#: tiramisu/option.py:1389 #: tiramisu/option.py:1526
msgid "{0}_params should be tuple for key \"{1}\"" msgid "{0}_params must be tuple for key \"{1}\""
msgstr "" msgstr ""
#: tiramisu/option.py:1395 #: tiramisu/option.py:1532
msgid "validator not support tuple" msgid "validator not support tuple"
msgstr "" msgstr ""
#: tiramisu/option.py:1398 #: tiramisu/option.py:1535
msgid "{0}_params should have an option not a {0} for first argument" msgid "{0}_params must have an option not a {0} for first argument"
msgstr "" msgstr ""
#: tiramisu/option.py:1402 #: tiramisu/option.py:1539
msgid "{0}_params should have a boolean not a {0} for second argument" msgid "{0}_params must have a boolean not a {0} for second argument"
msgstr ""
#: tiramisu/setting.py:111
msgid "can't rebind {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:116 #: tiramisu/setting.py:116
msgid "can't rebind {0}"
msgstr ""
#: tiramisu/setting.py:121
msgid "can't unbind {0}" msgid "can't unbind {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:254 #: tiramisu/setting.py:272
msgid "cannot append {0} property for option {1}: this property is calculated" msgid "cannot append {0} property for option {1}: this property is calculated"
msgstr "" msgstr ""
#: tiramisu/setting.py:317 #: tiramisu/setting.py:363
msgid "opt and all_properties must not be set together in reset" msgid "opt and all_properties must not be set together in reset"
msgstr "" msgstr ""
#: tiramisu/setting.py:332 #: tiramisu/setting.py:378
msgid "if opt is not None, path should not be None in _getproperties" msgid "if opt is not None, path should not be None in _getproperties"
msgstr "" msgstr ""
#: tiramisu/setting.py:435 #: tiramisu/setting.py:487
msgid "cannot change the value for option {0} this option is frozen" msgid "cannot change the value for option {0} this option is frozen"
msgstr "" msgstr ""
#: tiramisu/setting.py:441 #: tiramisu/setting.py:493
msgid "trying to access to an option named: {0} with properties {1}" msgid "trying to access to an option named: {0} with properties {1}"
msgstr "" msgstr ""
#: tiramisu/setting.py:459 #: tiramisu/setting.py:511
msgid "permissive must be a tuple" msgid "permissive must be a tuple"
msgstr "" msgstr ""
#: tiramisu/setting.py:466 tiramisu/value.py:299 #: tiramisu/setting.py:518 tiramisu/value.py:334
msgid "invalid generic owner {0}" msgid "invalid generic owner {0}"
msgstr "" msgstr ""
#: tiramisu/setting.py:553 #: tiramisu/setting.py:606
msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'" msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'"
msgstr "" msgstr ""
#: tiramisu/setting.py:565 #: tiramisu/setting.py:617
msgid "option '{0}' has requirement's property error: {1} {2}" msgid "option '{0}' has requirement's property error: {1} {2}"
msgstr "" msgstr ""
#: tiramisu/storage/__init__.py:47 #: tiramisu/storage/__init__.py:52
msgid "storage_type is already set, cannot rebind it" msgid "storage_type is already set, cannot rebind it"
msgstr "" msgstr ""
#: tiramisu/storage/__init__.py:81 #: tiramisu/storage/__init__.py:86
msgid "option {0} not already exists in storage {1}" msgid "option {0} not already exists in storage {1}"
msgstr "" msgstr ""
#: tiramisu/storage/dictionary/storage.py:37 #: tiramisu/storage/dictionary/storage.py:39
msgid "dictionary storage cannot delete session" msgid "dictionary storage cannot delete session"
msgstr "" msgstr ""
#: tiramisu/storage/dictionary/storage.py:48 #: tiramisu/storage/dictionary/storage.py:50
msgid "session already used" msgid "session already used"
msgstr "" msgstr ""
#: tiramisu/storage/dictionary/storage.py:50 #: tiramisu/storage/dictionary/storage.py:52
msgid "a dictionary cannot be persistent" msgid "a dictionary cannot be persistent"
msgstr "" msgstr ""
#: tiramisu/value.py:306 #: tiramisu/value.py:341
msgid "no value for {0} cannot change owner to {1}" msgid "no value for {0} cannot change owner to {1}"
msgstr "" msgstr ""
#: tiramisu/value.py:414 #: tiramisu/value.py:423
msgid "can force cache only if cache is actived in config"
msgstr ""
#: tiramisu/value.py:462
msgid "{0} is already a Multi "
msgstr ""
#: tiramisu/value.py:498 tiramisu/value.py:562
msgid "invalid len for the slave: {0} which has {1} as master" msgid "invalid len for the slave: {0} which has {1} as master"
msgstr "" msgstr ""
#: tiramisu/value.py:438 #: tiramisu/value.py:534
msgid "invalid len for the master: {0} which has {1} as slave with greater len"
msgstr ""
#: tiramisu/value.py:468
msgid "cannot append a value on a multi option {0} which is a slave" msgid "cannot append a value on a multi option {0} which is a slave"
msgstr "" msgstr ""
#: tiramisu/value.py:505 #: tiramisu/value.py:572
msgid "cannot sort multi option {0} if master or slave" msgid "cannot sort multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:509 #: tiramisu/value.py:576
msgid "cmp is not permitted in python v3 or greater" msgid "cmp is not permitted in python v3 or greater"
msgstr "" msgstr ""
#: tiramisu/value.py:518 #: tiramisu/value.py:585
msgid "cannot reverse multi option {0} if master or slave" msgid "cannot reverse multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:526 #: tiramisu/value.py:593
msgid "cannot insert multi option {0} if master or slave" msgid "cannot insert multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:534 #: tiramisu/value.py:601
msgid "cannot extend multi option {0} if master or slave" msgid "cannot extend multi option {0} if master or slave"
msgstr "" msgstr ""
#: tiramisu/value.py:562 #: tiramisu/value.py:612
msgid "invalid value {0} for option {1}: {2}"
msgstr ""
#: tiramisu/value.py:630
msgid "cannot pop a value on a multi option {0} which is a slave" msgid "cannot pop a value on a multi option {0} which is a slave"
msgstr "" msgstr ""