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.config.Config` which is the whole configuration entry point
.. image:: config.png
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`),
are organized into a tree into nested
:class:`~tiramisu.option.OptionDescription` objects. 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``.
:class:`~tiramisu.option.OptionDescription` objects.
.. 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
(because explicit is better than implicit):
@ -362,6 +364,10 @@ read/write or read only mode::
>>> c.cfgimpl_get_settings().remove('unknown')
>>> print c.od1.var3
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
: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
from tiramisu.option import IntOption, OptionDescription
from tiramisu.config import Config
from tiramisu.error import ConfigError
from time import sleep, time
from py.test import raises
def make_description():
@ -253,3 +257,25 @@ def test_reset_cache_only():
c.cfgimpl_reset_cache(only=('settings',))
assert 'u1' in values._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
from py.test import raises
from tiramisu.config import Config
from tiramisu.config import Config, SubConfig
from tiramisu.option import IntOption, FloatOption, StrOption, ChoiceOption, \
BoolOption, UnicodeOption, OptionDescription
from tiramisu.error import ConflictError
from tiramisu.error import ConflictError, ConfigError
import weakref
def make_description():
@ -131,8 +132,10 @@ def test_cfgimpl_get_home_by_path():
config.bool = False
assert config.cfgimpl_get_home_by_path('gc.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():
@ -142,6 +145,7 @@ def test_information_config():
config.impl_set_information('info', string)
assert config.impl_get_information('info') == string
raises(ValueError, "config.impl_get_information('noinfo')")
assert config.impl_get_information('noinfo', 'default') == 'default'
def test_config_impl_get_path_by_opt():
@ -149,8 +153,10 @@ def test_config_impl_get_path_by_opt():
config = Config(descr)
dummy = config.unwrap_from_path('gc.dummy')
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(dummy) == 'gc.dummy'
raises(AttributeError, "config.cfgimpl_get_description().impl_get_path_by_opt(unknown)")
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')
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
raises(AttributeError, "config.cfgimpl_get_description().impl_get_opt_by_path('gc.unknown')")
def test_information_display():
@ -231,8 +238,83 @@ def test_duplicated_option():
#in different OptionDescription
raises(ConflictError, "config = Config(root)")
def test_cannot_assign_value_to_option_description():
descr = make_description()
cfg = Config(descr)
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.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():
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False)
prop = BoolOption('prop', '', properties=('disabled',))
prop2 = BoolOption('prop', '', properties=('hidden',))
objspaceoption = ChoiceOption('objspace', 'Object space',
('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True)
booloption2 = BoolOption('bool', 'Test boolean option', default=False)
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)
stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Tests', 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,
wantref_option, stroption,
wantframework_option,
intoption, boolop])
intoption, boolop, floatoption2])
return descr
@ -62,6 +70,17 @@ def test_iter_config():
[('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():
"iteration on config sub object"
descr = make_description()
@ -96,24 +115,57 @@ def test_make_dict():
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():
"finds option in config"
descr = make_description()
conf = Config(descr)
conf.read_only()
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='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_first(bytype=ChoiceOption) == 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(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
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(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
#assert conf.find_first(byattrs= dict(default=2.3)) == conf.unwrap_from_path('gc.float')
#assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy')
#subconfig
assert conf.gc.find(byname='dummy') == [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():
@ -137,3 +189,81 @@ def test_does_not_find_in_config():
descr = make_description()
conf = Config(descr)
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 tiramisu.config import Config
from tiramisu.option import DomainnameOption, OptionDescription
from tiramisu.option import DomainnameOption, EmailOption, URLOption, OptionDescription
def test_domainname():
d = DomainnameOption('d', '')
e = DomainnameOption('e', '', "toto.com")
od = OptionDescription('a', '', [d, e])
f = DomainnameOption('f', '', allow_without_dot=True)
g = DomainnameOption('g', '', allow_ip=True)
od = OptionDescription('a', '', [d, f, g])
c = Config(od)
c.read_write()
c.d = 'toto.com'
raises(ValueError, "c.d = 'toto'")
c.d = 'toto3.com'
c.d = 'toto3.3la'
raises(ValueError, "c.d = '3toto.com'")
c.d = 'toto.co3'
raises(ValueError, "c.d = 'toto3.3la'")
#raises(ValueError, "c.d = '3toto.com'")
raises(ValueError, "c.d = 'toto.co3'")
raises(ValueError, "c.d = 'toto_super.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():
@ -41,3 +65,35 @@ def test_domainname_hostname():
raises(ValueError, "c.d = 'toto.com'")
c.d = 'toto'
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'
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():
a = IPOption('a', '', '88.88.88.88')

View File

@ -2,8 +2,8 @@
import autopath
#from py.test import raises
from tiramisu.config import Config
from tiramisu.option import BoolOption, OptionDescription
from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.option import BoolOption, IntOption, OptionDescription
import weakref
@ -109,3 +109,31 @@ def test_deref_optiondescription_config():
assert w() is not None
del(c)
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
#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.error import PropertiesOptionError
@ -205,11 +205,11 @@ def test_mandatory_warnings_ro():
except PropertiesOptionError as err:
proc = err.proptype
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.str = 'a'
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():
@ -218,9 +218,9 @@ def test_mandatory_warnings_rw():
config.str = ''
config.read_write()
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'
assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3']
assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3']
def test_mandatory_warnings_disabled():
@ -230,9 +230,9 @@ def test_mandatory_warnings_disabled():
setting = config.cfgimpl_get_settings()
config.read_write()
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')
assert list(mandatory_warnings(config)) == ['str1', 'unicode2', 'str3']
assert list(config.cfgimpl_get_values().mandatory_warnings()) == ['str1', 'unicode2', 'str3']
def test_mandatory_warnings_frozen():
@ -242,7 +242,7 @@ def test_mandatory_warnings_frozen():
setting = config.cfgimpl_get_settings()
config.read_write()
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')
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.config import Config, MetaConfig
#from tiramisu.option import IntOption, OptionDescription
#from tiramisu.error import ConfigError
from tiramisu.setting import owners
from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.option import IntOption, OptionDescription
from tiramisu.error import ConfigError, PropertiesOptionError
#owners.addowner('meta')
owners.addowner('meta')
#def make_description():
# 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)
# meta = MetaConfig([conf1, conf2])
# meta.cfgimpl_get_settings().setowner(owners.meta)
# return meta
def make_description():
i1 = IntOption('i1', '')
i2 = IntOption('i2', '', default=1)
i3 = IntOption('i3', '')
i4 = IntOption('i4', '', default=2)
i5 = IntOption('i5', '', default=[2], multi=True)
i6 = IntOption('i6', '', properties=('disabled',))
od1 = OptionDescription('od1', '', [i1, i2, i3, i4, i5, i6])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
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 OD differents dans un meta
#def test_none():
# meta = make_description()
# conf1, conf2 = meta._impl_children
# 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
# meta.od1.i3 = 3
# assert conf1.od1.i3 == conf2.od1.i3 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
# meta.od1.i3 = 3
# conf1.od1.i3 = 2
# assert conf1.od1.i3 == 2
# assert conf2.od1.i3 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
# meta.od1.i3 = 4
# assert conf1.od1.i3 == 2
# assert conf2.od1.i3 == 4
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
# del(meta.od1.i3)
# assert conf1.od1.i3 == 2
# assert conf2.od1.i3 is None
# assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default
# del(conf1.od1.i3)
# 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
#FIXME ne pas mettre 2 meta dans une config
#FIXME ne pas mettre 2 OD differents dans un meta
#FIXME serialization
def test_none():
meta = make_description()
conf1, conf2 = meta.cfgimpl_get_children()
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
meta.od1.i3 = 3
assert conf1.od1.i3 == conf2.od1.i3 == 3
assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
meta.od1.i3 = 3
conf1.od1.i3 = 2
assert conf1.od1.i3 == 2
assert conf2.od1.i3 == 3
assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
meta.od1.i3 = 4
assert conf1.od1.i3 == 2
assert conf2.od1.i3 == 4
assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.meta
del(meta.od1.i3)
assert conf1.od1.i3 == 2
assert conf2.od1.i3 is None
assert conf1.getowner(conf1.unwrap_from_path('od1.i3')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i3')) is owners.default
del(conf1.od1.i3)
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():
# meta = make_description()
# conf1, conf2 = meta._impl_children
# 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
# meta.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
# meta.od1.i2 = 3
# conf1.od1.i2 = 2
# assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# meta.od1.i2 = 4
# assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 4
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# del(meta.od1.i2)
# assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# del(conf1.od1.i2)
# 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
def test_default():
meta = make_description()
conf1, conf2 = meta.cfgimpl_get_children()
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
meta.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
meta.od1.i2 = 3
conf1.od1.i2 = 2
assert conf1.od1.i2 == 2
assert conf2.od1.i2 == 3
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
meta.od1.i2 = 4
assert conf1.od1.i2 == 2
assert conf2.od1.i2 == 4
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
del(meta.od1.i2)
assert conf1.od1.i2 == 2
assert conf2.od1.i2 == 1
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
del(conf1.od1.i2)
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
#def test_contexts():
# meta = make_description()
# conf1, conf2 = meta._impl_children
# 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
# meta.set_contexts('od1.i2', 6)
# assert meta.od1.i2 == 1
# 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
def test_contexts():
meta = make_description()
conf1, conf2 = meta.cfgimpl_get_children()
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
meta.setattrs('od1.i2', 6)
assert meta.od1.i2 == 1
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
#def test_find():
# meta = make_description()
# i2 = meta.unwrap_from_path('od1.i2')
# assert [i2] == meta.find(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}
def test_find():
meta = make_description()
i2 = meta.unwrap_from_path('od1.i2')
assert [i2] == meta.find(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, 'od1.i5': [2], 'od1.i6': None}
#def test_meta_meta():
# meta1 = make_description()
# meta2 = MetaConfig([meta1])
# meta2.cfgimpl_get_settings().setowner(owners.meta)
# conf1, conf2 = meta1._impl_children
# 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
# meta2.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
# meta2.od1.i2 = 3
# conf1.od1.i2 = 2
# assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 3
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# meta2.od1.i2 = 4
# assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 4
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
# del(meta2.od1.i2)
# assert conf1.od1.i2 == 2
# assert conf2.od1.i2 == 1
# assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
# assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
# del(conf1.od1.i2)
# 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
# meta1.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
def test_meta_meta():
meta1 = make_description()
meta2 = MetaConfig([meta1])
meta2.cfgimpl_get_settings().setowner(owners.meta)
conf1, conf2 = meta1.cfgimpl_get_children()
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
meta2.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
meta2.od1.i2 = 3
conf1.od1.i2 = 2
assert conf1.od1.i2 == 2
assert conf2.od1.i2 == 3
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
meta2.od1.i2 = 4
assert conf1.od1.i2 == 2
assert conf2.od1.i2 == 4
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.meta
del(meta2.od1.i2)
assert conf1.od1.i2 == 2
assert conf2.od1.i2 == 1
assert conf1.getowner(conf1.unwrap_from_path('od1.i2')) is owners.user
assert conf2.getowner(conf2.unwrap_from_path('od1.i2')) is owners.default
del(conf1.od1.i2)
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
meta1.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
#def test_meta_meta_set():
# meta1 = make_description()
# meta2 = MetaConfig([meta1])
# meta2.cfgimpl_get_settings().setowner(owners.meta)
# conf1, conf2 = meta1._impl_children
# meta2.set_contexts('od1.i1', 7)
# 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
# assert [conf1, conf2] == meta2.find_first_contexts(byname='i1', byvalue=7)
# conf1.od1.i1 = 8
# assert [conf2] == meta2.find_first_contexts(byname='i1', byvalue=7)
# assert [conf1] == meta2.find_first_contexts(byname='i1', byvalue=8)
# raises(AttributeError, "meta2.find_first_contexts(byname='i1', byvalue=10)")
def test_meta_meta_set():
meta1 = make_description()
meta2 = MetaConfig([meta1])
meta2.cfgimpl_get_settings().setowner(owners.meta)
conf1, conf2 = meta1.cfgimpl_get_children()
meta2.setattrs('od1.i1', 7)
#PropertiesOptionError
meta2.setattrs('od1.i6', 7)
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
assert [conf1, conf2] == meta2.find_firsts(byname='i1', byvalue=7)
conf1.od1.i1 = 8
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():
# i1 = IntOption('i1', '')
# od1 = OptionDescription('od1', '', [i1])
# od2 = OptionDescription('od2', '', [od1])
# conf1 = Config(od2)
# conf2 = Config(od2)
# meta = MetaConfig([conf1, conf2], False)
# raises(ConfigError, 'meta.od1.i1')
# conf1, conf2 = meta._impl_children
# meta.set_contexts('od1.i1', 7)
# 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_not_meta():
i1 = IntOption('i1', '')
od1 = OptionDescription('od1', '', [i1])
od2 = OptionDescription('od2', '', [od1])
conf1 = Config(od2)
conf2 = Config(od2)
raises(ValueError, "GroupConfig(conf1)")
meta = GroupConfig([conf1, conf2])
raises(ConfigError, 'meta.od1.i1')
conf1, conf2 = meta.cfgimpl_get_children()
meta.setattrs('od1.i1', 7)
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():
# meta = make_description()
# assert meta._impl_path is None
# assert meta.od1._impl_path == 'od1'
def test_meta_path():
meta = make_description()
assert meta._impl_path is None
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
"""
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():
@ -36,3 +41,60 @@ from tiramisu.option import BoolOption, IntOption
# assert dummy1 != dummy5
# assert dummy1 == dummy6
# 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):
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):
@ -34,6 +40,10 @@ def return_value2(*args, **kwargs):
return value
def return_calc(i, j, k):
return i + j + k
def make_description():
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -93,83 +103,6 @@ def test_identical_paths():
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():
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -310,6 +243,19 @@ def test_callback():
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():
val1 = StrOption('val1', "", 'val')
val2 = StrOption('val2', "", callback=return_value, callback_params={'': ((val1, False),)})
@ -421,12 +367,15 @@ def test_callback_multi_value():
cfg.val1.append('new-val2')
assert cfg.val1 == ['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)
assert cfg.val1 == ['val']
assert cfg.val2 == ['val']
assert cfg.val3 == ['yes']
assert cfg.val4 == ['val', 'yes']
cfg.val2.append('new')
assert cfg.val1 == ['val']
assert cfg.val2 == ['val', 'new']
def test_callback_multi_list():
@ -443,6 +392,14 @@ def test_callback_multi_list():
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():
val1 = StrOption('val1', "", multi=True, callback=return_val)
val2 = StrOption('val2', "", multi=True)
@ -452,7 +409,7 @@ def test_callback_master_and_slaves_master():
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val1.val1 == ['val']
cfg.val1.val1.append(None)
cfg.val1.val1.append()
assert cfg.val1.val1 == ['val', 'val']
assert cfg.val1.val2 == [None, None]
@ -467,7 +424,7 @@ def test_callback_master_and_slaves_master_list():
cfg.read_write()
assert cfg.val1.val1 == ['val', 'val']
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.val2 == [None, None, None]
del(cfg.val1.val1)
@ -512,6 +469,66 @@ def test_callback_master_and_slaves_slave():
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():
val1 = StrOption('val1', "", multi=True)
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',)})
val4 = StrOption('val4', '', multi=True, default=['val10', 'val11'])
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)
maconfig = OptionDescription('rootconfig', '', [interface1, val4])
cfg = Config(maconfig)
@ -544,30 +562,35 @@ def test_callback_master_and_slaves_value():
assert cfg.val1.val2 == []
assert cfg.val1.val3 == []
assert cfg.val1.val5 == []
assert cfg.val1.val6 == []
#
cfg.val1.val1 = ['val1']
assert cfg.val1.val1 == ['val1']
assert cfg.val1.val2 == ['val1']
assert cfg.val1.val3 == ['yes']
assert cfg.val1.val5 == ['val10']
assert cfg.val1.val6 == ['val10']
#
cfg.val1.val1.append('val2')
assert cfg.val1.val1 == ['val1', 'val2']
assert cfg.val1.val2 == ['val1', 'val2']
assert cfg.val1.val3 == ['yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11']
assert cfg.val1.val6 == ['val10', 'val11']
#
cfg.val1.val1 = ['val1', 'val2', 'val3']
assert cfg.val1.val1 == ['val1', 'val2', 'val3']
assert cfg.val1.val2 == ['val1', 'val2', 'val3']
assert cfg.val1.val3 == ['yes', 'yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11', None]
assert cfg.val1.val6 == ['val10', 'val11', None]
#
cfg.val1.val1.pop(2)
assert cfg.val1.val1 == ['val1', 'val2']
assert cfg.val1.val2 == ['val1', 'val2']
assert cfg.val1.val3 == ['yes', 'yes']
assert cfg.val1.val5 == ['val10', 'val11']
assert cfg.val1.val6 == ['val10', 'val11']
#
cfg.val1.val2 = ['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.val3 == ['val2', 'val2']
assert cfg.val1.val5 == ['val2', 'val2']
assert cfg.val1.val6 == ['val2', 'val2']
#
cfg.val1.val1.append('val3')
assert cfg.val1.val2 == ['val2', 'val2', 'val3']
assert cfg.val1.val3 == ['val2', 'val2', 'yes']
assert cfg.val1.val5 == ['val2', 'val2', None]
assert cfg.val1.val6 == ['val2', 'val2', None]
cfg.cfgimpl_get_settings().remove('cache')
cfg.val4 = ['val10', 'val11', 'val12']
#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 = ['val1', 'val2', 'val3']
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():
@ -653,7 +741,7 @@ def test_callback_multi_list_params():
maconfig = OptionDescription('rootconfig', '', [val1, oval2])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val2.val2 == ['val', 'val', 'val', 'val']
assert cfg.val2.val2 == ['val', 'val']
def test_callback_multi_list_params_key():
@ -663,31 +751,4 @@ def test_callback_multi_list_params_key():
maconfig = OptionDescription('rootconfig', '', [val1, oval2])
cfg = Config(maconfig)
cfg.read_write()
assert cfg.val2.val2 == ['val', 'val', '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")
assert cfg.val2.val2 == ['val', 'val']

View File

@ -5,7 +5,32 @@ from tiramisu.setting import owners, groups
from tiramisu.config import Config
from tiramisu.option import IPOption, NetworkOption, NetmaskOption, IntOption,\
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():
@ -158,6 +183,41 @@ def test_consistency_network_netmask():
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():
a = IPOption('a', '', multi=True)
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.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']
@ -249,6 +311,20 @@ def test_consistency_broadcast():
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():
a = NetworkOption('a', '', '192.168.1.0')
b = NetmaskOption('b', '', '255.255.255.128')
@ -272,3 +348,28 @@ def test_consistency_not_all():
c.a = ['192.168.1.0']
c.b = ['255.255.255.0']
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.option import ChoiceOption, BoolOption, IntOption, FloatOption, \
StrOption, OptionDescription
from tiramisu.error import ConfigError
from tiramisu.error import ConfigError, ConstError
def make_description():
@ -36,6 +36,7 @@ def test_default_owner():
cfg = Config(descr)
assert cfg.dummy is False
assert cfg.getowner(gcdummy) == 'default'
raises(TypeError, "cfg.getowner('gcdummy')")
cfg.dummy = True
assert cfg.getowner(gcdummy) == owners.user
@ -52,6 +53,17 @@ def test_addowner():
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():
gcdummy = BoolOption('dummy', 'dummy', default=False)
descr = OptionDescription('tiramisu', '', [gcdummy])

View File

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

View File

@ -9,7 +9,8 @@ from tiramisu.error import PropertiesOptionError
def make_description():
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():
@ -91,3 +92,108 @@ def test_invalid_permissive():
setting = config.cfgimpl_get_settings()
config.read_write()
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:
props = err.proptype
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():
@ -504,6 +540,11 @@ def test_requires_requirement_append():
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():
a = BoolOption('activate_service', '', True)
b = IPOption('ip_address_service', '',

View File

@ -3,9 +3,10 @@ import autopath
from py.test import raises
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, \
PortOption, NetworkOption, NetmaskOption, DomainnameOption
PortOption, NetworkOption, NetmaskOption, DomainnameOption, EmailOption, \
URLOption, FilenameOption
def test_slots_option():
@ -35,6 +36,12 @@ def test_slots_option():
raises(AttributeError, "c.x = 1")
c = DomainnameOption('a', '')
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():
@ -49,7 +56,10 @@ def test_slots_option_readonly():
j = NetworkOption('j', '')
k = NetmaskOption('k', '')
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'
b._requires = 'b'
c._requires = 'c'
@ -62,6 +72,9 @@ def test_slots_option_readonly():
k._requires = 'k'
l._requires = 'l'
m._requires = 'm'
o._requires = 'o'
p._requires = 'p'
q._requires = 'q'
Config(m)
raises(AttributeError, "a._requires = 'a'")
raises(AttributeError, "b._requires = 'b'")
@ -75,6 +88,9 @@ def test_slots_option_readonly():
raises(AttributeError, "k._requires = 'k'")
raises(AttributeError, "l._requires = 'l'")
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():
@ -90,7 +106,10 @@ def test_slots_option_readonly_name():
j = NetworkOption('j', '')
k = NetmaskOption('k', '')
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, "b._name = 'b'")
raises(AttributeError, "c._name = 'c'")
@ -104,6 +123,9 @@ def test_slots_option_readonly_name():
raises(AttributeError, "k._name = 'k'")
raises(AttributeError, "l._name = 'l'")
raises(AttributeError, "m._name = 'm'")
raises(AttributeError, "o._name = 'o'")
raises(AttributeError, "p._name = 'p'")
raises(AttributeError, "q._name = 'q'")
def test_slots_description():

View File

@ -1,10 +1,13 @@
import autopath
from tiramisu.option import BoolOption, UnicodeOption, SymLinkOption, \
OptionDescription
from tiramisu.config import Config
IntOption, OptionDescription
from tiramisu.config import Config, GroupConfig, MetaConfig
from tiramisu.setting import owners
from tiramisu.storage import delete_session
from tiramisu.error import ConfigError
from pickle import dumps, loads
from py.test import raises
def return_value(value=None):
@ -90,10 +93,48 @@ def _diff_opt(opt1, opt2):
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():
b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
#u.impl_add_consistency('not_equal', b)
s = SymLinkOption('s', u)
o = OptionDescription('o', '', [b, u, s])
o1 = OptionDescription('o1', '', [o])
@ -107,6 +148,11 @@ def test_diff_opt():
_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():
b = BoolOption('b', '')
u = UnicodeOption('u', '', requires=[{'option': b, 'expected': True, 'action': 'disabled', 'inverse': True}])
@ -169,10 +215,7 @@ def test_state_config():
cfg._impl_test = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
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()
_diff_conf(cfg, q)
try:
delete_session('29090931')
except ConfigError:
@ -191,12 +234,9 @@ def test_state_properties():
cfg.cfgimpl_get_settings()[val1].append('test')
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
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()
_diff_conf(cfg, q)
try:
delete_session('29090931')
delete_session('29090932')
except ConfigError:
pass
@ -212,15 +252,12 @@ def test_state_values():
cfg.val1 = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
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()
_diff_conf(cfg, q)
q.val1 = False
#assert cfg.val1 is True
assert q.val1 is False
try:
delete_session('29090931')
delete_session('29090933')
except ConfigError:
pass
@ -238,14 +275,94 @@ def test_state_values_owner():
cfg.val1 = True
a = dumps(cfg)
q = loads(a)
_diff_opt(maconfig, q.cfgimpl_get_description())
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()
_diff_conf(cfg, q)
q.val1 = False
nval1 = q.cfgimpl_get_description().val1
assert q.getowner(nval1) == owners.newowner
try:
delete_session('29090931')
delete_session('29090934')
except ConfigError:
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"
from tiramisu.error import PropertiesOptionError, ConfigError
from tiramisu.setting import multitypes
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):
"""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
the whole options available
: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 ''.
* if no callback_params:
=> calculate()
=> calculate(<function func at 0x2092320>, [], {})
* if callback_params={'': ('yes',)}
=> calculate('yes')
=> calculate(<function func at 0x2092320>, ['yes'], {})
* if callback_params={'value': ('yes',)}
=> calculate(value='yes')
=> calculate(<function func at 0x165b320>, [], {'value': 'yes'})
* if callback_params={'': ('yes', 'no')}
=> calculate('yes', 'no')
@ -62,58 +63,71 @@ def carry_out_calculation(name, config, callback, callback_params,
* if callback_params={'value': ('yes', 'no')}
=> 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),)}
- a simple option:
opt1 == 11
=> calculate(11)
=> calculate(<function func at 0x1cea320>, [11], {})
- a multi option:
- a multi option and not master/slave:
opt1 == [1, 2, 3]
=> calculate(1)
=> calculate(2)
=> calculate(3)
=> calculate(<function func at 0x223c320>, [[1, 2, 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),)}
- a simple option:
opt1 == 11
=> calculate(value=11)
=> calculate(<function func at 0x17ff320>, [], {'value': 11})
- a multi option:
opt1 == [1, 2, 3]
=> calculate(value=1)
=> calculate(value=2)
=> calculate(value=3)
=> calculate(<function func at 0x1262320>, [], {'value': [1, 2, 3]})
* 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
opt1 == [1, 2, 3]
opt2 == 11
=> calculate(1, 11)
=> calculate(2, 11)
=> calculate(3, 11)
opt2 == 12
=> calculate(<function func at 0x2153320>, [[1, 2, 3], 12], {})
- a multi option with an other multi option but with same length
opt1 == [1, 2, 3]
opt2 == [11, 12, 13]
=> calculate(1, 11)
=> calculate(2, 12)
=> calculate(3, 13)
=> calculate(<function func at 0x1981320>, [[1, 2, 3], [11, 12, 13]], {})
- a multi option with an other multi option but with different length
opt1 == [1, 2, 3]
opt2 == [11, 12]
=> ConfigError()
=> calculate(<function func at 0x2384320>, [[1, 2, 3], [11, 12]], {})
- a multi option without value with a simple option
opt1 == []
opt2 == 11
=> []
=> calculate(<function func at 0xb65320>, [[], 12], {})
* if callback_params={'value': ((opt1, False), (opt2, False))}
=> ConfigError()
=> raises ValueError()
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:
if isinstance(callbk, tuple):
# 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(
option)
opt)
# get value
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:
if force_permissive:
continue
raise ConfigError(_('unable to carry out a calculation, '
'option {0} has properties: {1} for: '
'{2}').format(option._name,
'{2}').format(opt._name,
err.proptype,
name))
is_multi = option.impl_is_multi()
option._name))
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:
len_value = 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
len_multi = len(value)
one_is_multi = True
tcparams.setdefault(key, []).append((value, is_multi))
else:
@ -167,16 +188,9 @@ def carry_out_calculation(name, config, callback, callback_params,
if one_is_multi:
ret = []
if index:
if index < len_multi:
range_ = [index]
else:
range_ = []
ret = None
range_ = [index]
else:
if max_len and max_len < len_multi:
range_ = range(max_len)
else:
range_ = range(len_multi)
range_ = range(len_multi)
for incr in range_:
args = []
kwargs = {}
@ -195,10 +209,7 @@ def carry_out_calculation(name, config, callback, callback_params,
if index:
ret = calc
else:
if isinstance(calc, list):
ret.extend(calc)
else:
ret.append(calc)
ret.append(calc)
return ret
else:
# no value is multi
@ -212,7 +223,18 @@ def carry_out_calculation(name, config, callback, callback_params,
args.append(couple[0])
else:
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):

View File

@ -47,7 +47,7 @@ class SubConfig(object):
:type subpath: `str` with the path name
"""
# 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}'
).format(type(descr)))
self._impl_descr = descr
@ -148,19 +148,25 @@ class SubConfig(object):
except UnicodeEncodeError:
lines.append("{0} = {1}".format(name,
value.encode(default_encoding)))
except PropertiesOptionError:
pass
return '\n'.join(lines)
__repr__ = __str__
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):
if self._impl_descr is None:
raise ConfigError(_('no option description found for this config'
' (may be metaconfig without meta)'))
' (may be GroupConfig)'))
else:
return self._impl_descr
@ -249,7 +255,8 @@ class SubConfig(object):
force_properties=force_properties,
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
@ -261,11 +268,11 @@ class SubConfig(object):
return self._cfgimpl_get_context()._find(bytype, byname, byvalue,
first=False,
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,
type_='option', display_error=True):
type_='option', display_error=True, check_properties=True):
"""
finds an option recursively in the config
@ -276,7 +283,8 @@ class SubConfig(object):
"""
return self._cfgimpl_get_context()._find(
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',
_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
"""
def _filter_by_name():
try:
if byname is None or path == byname or \
path.endswith('.' + byname):
return True
except IndexError:
pass
if byname is None or path == byname or \
path.endswith('.' + byname):
return True
return False
def _filter_by_value():
@ -442,23 +447,20 @@ class SubConfig(object):
return pathsvalues
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,
_currpath +
path.split('.'))
except PropertiesOptionError:
pass # this just a hidden or disabled option
else:
try:
else:
value = self._getattr(opt._name)
if flatten:
name = opt._name
else:
name = '.'.join(_currpath + [opt._name])
pathsvalues.append((name, value))
except PropertiesOptionError:
pass # this just a hidden or disabled option
except PropertiesOptionError:
pass
def cfgimpl_get_path(self):
descr = self.cfgimpl_get_description()
@ -466,9 +468,9 @@ class SubConfig(object):
return context_descr.impl_get_path_by_opt(descr)
class CommonConfig(SubConfig):
"abstract base class for the Config and the MetaConfig"
__slots__ = ('_impl_values', '_impl_settings', '_impl_meta')
class _CommonConfig(SubConfig):
"abstract base class for the Config, GroupConfig and the MetaConfig"
__slots__ = ('_impl_values', '_impl_settings', '_impl_meta', '_impl_test')
def _impl_build_all_paths(self):
self.cfgimpl_get_description().impl_build_cache()
@ -507,7 +509,8 @@ class CommonConfig(SubConfig):
return None
def cfgimpl_get_meta(self):
return self._impl_meta
if self._impl_meta is not None:
return self._impl_meta()
# information
def impl_set_information(self, key, value):
@ -525,11 +528,48 @@ class CommonConfig(SubConfig):
"""
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"
__slots__ = ('__weakref__', '_impl_test')
__slots__ = ('__weakref__',)
def __init__(self, descr, session_id=None, persistent=False):
""" Configuration option management master class
@ -553,41 +593,6 @@ class Config(CommonConfig):
#undocumented option used only in test script
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,
only_expired=False,
only=('values', 'settings')):
@ -597,115 +602,121 @@ class Config(CommonConfig):
self.cfgimpl_get_settings().reset_cache(only_expired=only_expired)
#class MetaConfig(CommonConfig):
# __slots__ = ('_impl_children',)
class GroupConfig(_CommonConfig):
__slots__ = ('_impl_children', '__weakref__')
# def __init__(self, children, meta=True, session_id=None, persistent=False):
# if not isinstance(children, list):
# raise ValueError(_("metaconfig's children must be a list"))
# self._impl_descr = None
# self._impl_path = None
# if meta:
# for child in children:
# if not isinstance(child, CommonConfig):
# raise TypeError(_("metaconfig's children "
# "must be config, not {0}"
# ).format(type(child)))
# if self._impl_descr is None:
# 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
def __init__(self, children, session_id=None, persistent=False,
_descr=None):
if not isinstance(children, list):
raise ValueError(_("metaconfig's children must be a list"))
self._impl_children = children
settings, values = get_storages(self, session_id, persistent)
self._impl_settings = Settings(self, settings)
self._impl_values = Values(self, values)
super(GroupConfig, self).__init__(_descr, weakref.ref(self))
self._impl_meta = None
#undocumented option used only in test script
self._impl_test = False
# self._impl_children = children
# settings, values = get_storages(self, session_id, persistent)
# self._impl_settings = Settings(self, settings)
# self._impl_values = Values(self, values)
# self._impl_meta = None
def cfgimpl_get_children(self):
return self._impl_children
# def cfgimpl_get_children(self):
# return self._impl_children
#def cfgimpl_get_context(self):
# "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):
# "a meta config is a config wich has a setting, that is itself"
# return self
def setattrs(self, path, value):
"""Setattr not in current GroupConfig, but in each children
"""
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,
# 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 find_firsts(self, byname=None, bypath=None, byvalue=None,
type_='path', display_error=True):
"""Find first not in current GroupConfig, but in each children
"""
ret = []
#if MetaConfig, all children have same OptionDescription as context
#so search only one time for all children
try:
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,
# type_='path', display_error=True):
# ret = []
# try:
# if bypath is None and byname is not None and \
# self.cfgimpl_get_description() is not None:
# bypath = self._find(bytype=None, byvalue=None, byname=byname,
# first=True, type_='path',
# check_properties=False,
# display_error=display_error)
# except ConfigError:
# pass
# for child in self._impl_children:
# try:
# if not isinstance(child, MetaConfig):
# if bypath is not None:
# if byvalue is not None:
# if getattr(child, bypath) == byvalue:
# ret.append(child)
# else:
# #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)
class MetaConfig(GroupConfig):
__slots__ = tuple()
def __init__(self, children, session_id=None, persistent=False):
descr = None
for child in children:
if not isinstance(child, _CommonConfig):
raise TypeError(_("metaconfig's children "
"should be config, not {0}"
).format(type(child)))
if child.cfgimpl_get_meta() is not None:
raise ValueError(_("child has already a metaconfig's"))
if descr is None:
descr = child.cfgimpl_get_description()
elif not descr is child.cfgimpl_get_description():
raise ValueError(_('all config in metaconfig must '
'have the same optiondescription'))
child._impl_meta = weakref.ref(self)
super(MetaConfig, self).__init__(children, session_id, persistent, descr)
def mandatory_warnings(config):
"""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
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',))
#only for retro-compatibility
config.cfgimpl_get_values().mandatory_warnings()

View File

@ -39,9 +39,7 @@ forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
def valid_name(name):
"an option's name is a str and does not start with 'impl' or 'cfgimpl'"
try:
name = str(name)
except:
if not isinstance(name, str):
return False
if re.match(name_regexp, name) is None and not name.startswith('_') \
and name not in forbidden_names \
@ -337,7 +335,7 @@ class Option(BaseOption):
self._consistencies = None
def _launch_consistency(self, func, option, value, context, index,
all_cons_opts):
all_cons_opts, warnings_only):
"""Launch consistency now
:param func: function name, this name should start with _cons_
@ -352,12 +350,11 @@ class Option(BaseOption):
:type index: `int`
:param all_cons_opts: all options concerne by this consistency
: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:
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 = []
for opt in all_cons_opts:
@ -368,7 +365,8 @@ class Option(BaseOption):
#if context, calculate value, otherwise get default value
if context is not None:
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:
opt_value = opt.impl_getdefault()
@ -382,7 +380,7 @@ class Option(BaseOption):
except IndexError:
#so return if no value
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,
force_index=None):
@ -412,7 +410,7 @@ class Option(BaseOption):
else:
validator_params = {'': (val,)}
# Raise ValueError if not valid
carry_out_calculation(self._name, config=context,
carry_out_calculation(self, config=context,
callback=self._validator[0],
callback_params=validator_params)
@ -420,23 +418,41 @@ class Option(BaseOption):
if _value is None:
return
# 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:
# valid with self._validator
val_validator(_value)
# if not context launch consistency validation
if context is not None:
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)
self._second_level_validation(_value, self._warnings_only)
except ValueError as error:
if self._warnings_only:
warnings.warn_explicit(ValueWarning(msg, self),
ValueWarning,
self.__class__.__name__, 0)
else:
raise ValueError(msg)
warning = error
error = None
except ValueWarning as warning:
pass
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
if context is not None:
@ -446,17 +462,13 @@ class Option(BaseOption):
do_validation(value, force_index)
else:
if not isinstance(value, list):
raise ValueError(_("which must be a list").format(value,
self._name))
raise ValueError(_("invalid value {0} for option {1} which must be a list").format(value, self._name))
for index, val in enumerate(value):
do_validation(val, index)
def impl_getdefault(self, default_multi=False):
def impl_getdefault(self):
"accessing the default value"
if not default_multi or not self.impl_is_multi():
return self._default
else:
return self.getdefault_multi()
return self._default
def impl_getdefault_multi(self):
"accessing the default value for a multi"
@ -493,7 +505,7 @@ class Option(BaseOption):
def impl_is_multi(self):
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
option's values.
@ -501,16 +513,18 @@ class Option(BaseOption):
:type func: `str`
:param other_opts: options used to validate value
:type other_opts: `list` of `tiramisu.option.Option`
:param params: extra params (only warnings_only are allowed)
"""
if self._consistencies is None:
self._consistencies = []
warnings_only = params.get('warnings_only', False)
for opt in other_opts:
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:
raise ConfigError(_('cannot add consistency with itself'))
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'))
func = '_cons_{0}'.format(func)
all_cons_opts = tuple([self] + list(other_opts))
@ -519,19 +533,23 @@ class Option(BaseOption):
if self.impl_is_multi():
for idx, val in enumerate(value):
self._launch_consistency(func, self, val, None,
idx, all_cons_opts)
idx, all_cons_opts, warnings_only)
else:
self._launch_consistency(func, self, value, None,
None, all_cons_opts)
self._consistencies.append((func, all_cons_opts))
None, all_cons_opts, warnings_only)
self._consistencies.append((func, all_cons_opts, params))
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_sup, val_sup in enumerate(vals[idx_inf + 1:]):
if val_inf == val_sup is not None:
raise ValueError(_("same value for {0} and {1}").format(
opts[idx_inf]._name, opts[idx_inf + idx_sup + 1]._name))
if warnings_only:
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):
if not load and self._callback is None:
@ -587,38 +605,22 @@ class Option(BaseOption):
consistencies = self._state_consistencies
else:
consistencies = self._consistencies
if isinstance(consistencies, list):
new_value = []
for consistency in consistencies:
values = []
for obj in consistency[1]:
if load:
values.append(descr.impl_get_opt_by_path(obj))
else:
values.append(descr.impl_get_path_by_opt(obj))
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)))
new_value = []
for consistency in consistencies:
values = []
for obj in consistency[1]:
if load:
values.append(descr.impl_get_opt_by_path(obj))
else:
values.append(descr.impl_get_path_by_opt(obj))
new_value.append((consistency[0], tuple(values), consistency[2]))
if load:
del(self._state_consistencies)
self._consistencies = new_value
else:
self._state_consistencies = new_value
def _second_level_validation(self, value):
def _second_level_validation(self, value, warnings_only):
pass
@ -663,7 +665,7 @@ class ChoiceOption(Option):
return self._open_values
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, '
'only {1} is allowed'
'').format(value, self._values))
@ -676,7 +678,7 @@ class BoolOption(Option):
def _validate(self, value):
if not isinstance(value, bool):
raise ValueError(_('value must be a boolean'))
raise ValueError(_('invalid boolean'))
class IntOption(Option):
@ -686,7 +688,7 @@ class IntOption(Option):
def _validate(self, value):
if not isinstance(value, int):
raise ValueError(_('value must be an integer'))
raise ValueError(_('invalid integer'))
class FloatOption(Option):
@ -696,7 +698,7 @@ class FloatOption(Option):
def _validate(self, value):
if not isinstance(value, float):
raise ValueError(_('value must be a float'))
raise ValueError(_('invalid float'))
class StrOption(Option):
@ -706,12 +708,11 @@ class StrOption(Option):
def _validate(self, value):
if not isinstance(value, str):
raise ValueError(_('value must be a string, not '
'{0}').format(type(value)))
raise ValueError(_('invalid string'))
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):
__slots__ = tuple()
pass
@ -724,7 +725,7 @@ else:
def _validate(self, value):
if not isinstance(value, unicode):
raise ValueError(_('value must be an unicode'))
raise ValueError(_('invalid unicode'))
class SymLinkOption(BaseOption):
@ -782,17 +783,51 @@ class IPOption(Option):
warnings_only=warnings_only)
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:
IP('{0}/32'.format(value))
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))
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':
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):
@ -852,17 +887,23 @@ class PortOption(Option):
if self._allow_range and ":" in str(value):
value = str(value).split(':')
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]:
raise ValueError('first port in range must be'
' smaller than the second one')
raise ValueError(_('invalid port, first port in range must be'
' smaller than the second one'))
else:
value = [value]
for val in value:
try:
int(val)
except ValueError:
raise ValueError(_('invalid port'))
if not self._min_value <= int(val) <= self._max_value:
raise ValueError('port must be an between {0} and {1}'
''.format(self._min_value, self._max_value))
raise ValueError(_('invalid port, must be an between {0} '
'and {1}').format(self._min_value,
self._max_value))
class NetworkOption(Option):
@ -874,12 +915,16 @@ class NetworkOption(Option):
try:
IP(value)
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)
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):
@ -891,21 +936,22 @@ class NetmaskOption(Option):
try:
IP('0.0.0.0/{0}'.format(value))
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
if None in vals:
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
if None in vals:
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:
raise ConfigError(_('invalid len for opts'))
msg = None
@ -918,23 +964,18 @@ class NetmaskOption(Option):
IP('{0}/{1}'.format(val_ipnetwork, val_netmask),
make_net=not make_net)
except ValueError:
if not make_net:
msg = _("invalid network {0} ({1}) "
"with netmask {2} ({3}),"
" this network is an IP")
pass
else:
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")
except ValueError:
if make_net:
msg = _("invalid IP {0} ({1}) with netmask {2} ({3})")
else:
msg = _("invalid network {0} ({1}) with netmask {2} ({3})")
if not make_net:
msg = _('invalid network {0} ({1}) with netmask {2}')
if msg is not None:
raise ValueError(msg.format(val_ipnetwork, opts[1]._name,
val_netmask, self._name))
val_netmask))
class BroadcastOption(Option):
@ -945,9 +986,9 @@ class BroadcastOption(Option):
try:
IP('{0}/32'.format(value))
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:
raise ConfigError(_('invalid len for vals'))
if None in vals:
@ -967,20 +1008,44 @@ class DomainnameOption(Option):
domainname:
fqdn: with tld, not supported yet
"""
__slots__ = ('_type', '_allow_ip')
__slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
_opt_type = 'domainname'
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_params=None,
properties=None, allow_ip=False, type_='domainname',
warnings_only=False):
warnings_only=False, allow_without_dot=False):
if type_ not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
self._type = type_
if allow_ip not in [True, False]:
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_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,
default_multi=default_multi,
callback=callback,
@ -999,29 +1064,94 @@ class DomainnameOption(Option):
return
except ValueError:
pass
if self._type == 'netbios':
length = 15
extrachar = ''
elif self._type == 'hostname':
length = 63
extrachar = ''
elif self._type == 'domainname':
length = 255
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:
if self._type == 'domainname' and not self._allow_without_dot and \
'.' not in value:
raise ValueError(_("invalid domainname, must have dot"))
if len(value) > 255:
raise ValueError(_("invalid domainname's length (max 255)"))
if len(value) < 2:
raise ValueError(_("invalid domainname's length (min 2)"))
if not self._domain_re.search(value):
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):
"""Config's schema (organisation, group) and container of Options
The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
@ -1125,11 +1255,12 @@ class OptionDescription(BaseOption):
if not force_no_consistencies and \
option._consistencies is not None:
for consistency in option._consistencies:
func, all_cons_opts = consistency
func, all_cons_opts, params = consistency
for opt in all_cons_opts:
_consistencies.setdefault(opt,
[]).append((func,
all_cons_opts))
all_cons_opts,
params))
else:
_currpath.append(attr)
option.impl_build_cache(cache_path,
@ -1176,7 +1307,6 @@ class OptionDescription(BaseOption):
self._group_type = group_type
if isinstance(group_type, groups.MasterGroupType):
#if master (same name has group) is set
identical_master_child_name = False
#for collect all slaves
slaves = []
master = None
@ -1193,7 +1323,6 @@ class OptionDescription(BaseOption):
": this option is not a multi"
"").format(child._name, self._name))
if child._name == self._name:
identical_master_child_name = True
child._multitype = multitypes.master
master = child
else:
@ -1202,14 +1331,18 @@ class OptionDescription(BaseOption):
raise ValueError(_('master group with wrong'
' master name for {0}'
).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)
for child in self.impl_getchildren():
if child != master:
child._master_slaves = master
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:
raise ValueError(_('group_type: {0}'
' not allowed').format(group_type))
@ -1223,15 +1356,20 @@ class OptionDescription(BaseOption):
#consistencies is something like [('_cons_not_equal', (opt1, opt2))]
consistencies = self._cache_consistencies.get(option)
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
ret = all_cons_opts[0]._launch_consistency(func, option,
value,
context, index,
all_cons_opts)
if ret is False:
return False
return True
try:
all_cons_opts[0]._launch_consistency(func, option,
value,
context, index,
all_cons_opts,
warnings_only)
except ValueError as err:
if warnings_only:
raise ValueWarning(err.message, option)
else:
raise err
def _impl_getstate(self, descr=None):
"""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))
if option.impl_is_multi():
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:
try:
option._validate(expected)
@ -1376,17 +1514,17 @@ def validate_requires_arg(requires, name):
def validate_callback(callback, callback_params, type_):
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 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():
if key != '' and len(callbacks) != 1:
raise ValueError(_('{0}_params with key {1} should not have '
'length different to 1').format(type_,
raise ValueError(_("{0}_params with key {1} mustn't have "
"length different to 1").format(type_,
key))
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))
for callbk in callbacks:
if isinstance(callbk, tuple):
@ -1395,11 +1533,11 @@ def validate_callback(callback, callback_params, type_):
raise ValueError(_('validator not support tuple'))
if not isinstance(option, Option) and not \
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'
).format(type_, type(option)))
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'
).format(type_, type(
force_permissive)))

View File

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

View File

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

View File

@ -31,7 +31,7 @@ class Settings(Sqlite3DB):
self._storage.execute(settings_table, commit=False)
self._storage.execute(permissives_table)
# propertives
# properties
def setproperties(self, path, properties):
path = self._sqlite_encode_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 "
"path = ?", (path,)) is not None
def reset_all_propertives(self):
def reset_all_properties(self):
self._storage.execute("DELETE FROM property")
def reset_properties(self, path):

View File

@ -19,8 +19,8 @@ from time import time
from copy import copy
import sys
import weakref
from tiramisu.error import ConfigError, SlaveError
from tiramisu.setting import owners, multitypes, expires_time
from tiramisu.error import ConfigError, SlaveError, PropertiesOptionError
from tiramisu.setting import owners, multitypes, expires_time, undefined
from tiramisu.autolib import carry_out_calculation
from tiramisu.i18n import _
from tiramisu.option import SymLinkOption
@ -44,15 +44,28 @@ class Values(object):
# the storage type is dictionary or sqlite3
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):
"""
actually retrieves the default value
:param opt: the `option.Option()` object
"""
meta = self.context().cfgimpl_get_meta()
meta = self._getcontext().cfgimpl_get_meta()
if meta is not None:
value = meta.cfgimpl_get_values()[opt]
if isinstance(value, Multi):
value = list(value)
else:
value = opt.impl_getdefault()
if opt.impl_is_multi():
@ -60,7 +73,7 @@ class Values(object):
else:
return value
def _getvalue(self, opt, path, validate=True):
def _getvalue(self, opt, path):
"""actually retrieves the value
:param opt: the `option.Option()` object
@ -69,14 +82,9 @@ class Values(object):
if not self._p_.hasvalue(path):
# if there is no value
value = self._getdefault(opt)
if opt.impl_is_multi():
value = Multi(value, self.context, opt, path, validate)
else:
# if there is a value
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
def get_modified_values(self):
@ -103,11 +111,11 @@ class Values(object):
if path is None:
path = self._get_opt_path(opt)
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(),
self.context(),
'validator' in setting)
self.context().cfgimpl_reset_cache()
context, 'validator' in setting)
context.cfgimpl_reset_cache()
if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.master):
for slave in opt.impl_get_master_slaves():
@ -135,7 +143,7 @@ class Values(object):
callback, callback_params = opt._callback
if callback_params is None:
callback_params = {}
return carry_out_calculation(opt._name, config=self.context(),
return carry_out_calculation(opt, config=self._getcontext(),
callback=callback,
callback_params=callback_params,
index=index, max_len=max_len)
@ -149,7 +157,7 @@ class Values(object):
if path is None:
path = self._get_opt_path(opt)
ntime = None
setting = self.context().cfgimpl_get_settings()
setting = self._getcontext().cfgimpl_get_settings()
if 'cache' in setting and self._p_.hascache(path):
if 'expire' in setting:
ntime = int(time())
@ -174,7 +182,8 @@ class Values(object):
def _getitem(self, opt, path, validate, force_permissive, force_properties,
validate_properties):
# options with callbacks
setting = self.context().cfgimpl_get_settings()
context = self._getcontext()
setting = context.cfgimpl_get_settings()
is_frozen = 'frozen' in setting[opt]
# For calculating properties, we need value (ie for mandatory value).
# If value is calculating with a PropertiesOptionError's option
@ -184,7 +193,7 @@ class Values(object):
# ConfigError if properties did not raise.
config_error = 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
if opt.impl_has_callback() and (
self._is_default_owner(path) or
@ -194,7 +203,7 @@ class Values(object):
if (opt.impl_is_multi() and
opt.impl_get_multitype() == multitypes.slave):
masterp = self._get_opt_path(opt.impl_get_master_slaves())
mastervalue = getattr(self.context(), masterp)
mastervalue = context._getattr(masterp, validate=validate)
lenmaster = len(mastervalue)
if lenmaster == 0:
value = []
@ -226,9 +235,12 @@ class Values(object):
if opt.impl_is_multi():
value = Multi(value, self.context, opt, path, validate)
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:
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 \
'force_store_value' in setting[opt]:
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"
# user didn't change value, so not write
# valid opt
opt.impl_validate(value, self.context(),
'validator' in self.context().cfgimpl_get_settings())
if opt.impl_is_multi() and not isinstance(value, Multi):
context = self._getcontext()
opt.impl_validate(value, context,
'validator' in context.cfgimpl_get_settings())
if opt.impl_is_multi():
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,
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,
force_properties=None,
is_write=True, validate_properties=True):
self.context().cfgimpl_reset_cache()
context = self._getcontext()
context.cfgimpl_reset_cache()
if validate_properties:
setting = self.context().cfgimpl_get_settings()
setting = context.cfgimpl_get_settings()
setting.validate_properties(opt, False, is_write,
value=value, path=path,
force_permissive=force_permissive,
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)
def getowner(self, opt):
@ -283,7 +316,7 @@ class Values(object):
def _getowner(self, path):
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:
owner = meta.cfgimpl_get_values()._getowner(path)
return owner
@ -335,7 +368,7 @@ class Values(object):
:param opt: the `option.Option` object
: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
def set_information(self, key, value):
@ -360,6 +393,42 @@ class Values(object):
raise ValueError(_("information's item"
" 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):
return {'_p_': self._p_}
@ -387,6 +456,8 @@ class Multi(list):
:param opt: the option object that have this Multi 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.path = path
if not isinstance(context, weakref.ReferenceType):
@ -396,21 +467,32 @@ class Multi(list):
value = [value]
if validate and self.opt.impl_get_multitype() == multitypes.slave:
value = self._valid_slave(value, setitem)
elif validate and self.opt.impl_get_multitype() == multitypes.master:
self._valid_master(value)
elif not setitem and validate and \
self.opt.impl_get_multitype() == multitypes.master:
self._valid_master()
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):
#if slave, had values until master's one
values = self.context().cfgimpl_get_values()
masterp = self.context().cfgimpl_get_description().impl_get_path_by_opt(
context = self._getcontext()
values = context.cfgimpl_get_values()
masterp = context.cfgimpl_get_description().impl_get_path_by_opt(
self.opt.impl_get_master_slaves())
mastervalue = getattr(self.context(), masterp)
mastervalue = context._getattr(masterp, validate=False)
masterlen = len(mastervalue)
valuelen = len(value)
is_default_owner = not values._is_default_owner(self.path) or setitem
if valuelen > masterlen or (valuelen < masterlen and
is_default_owner):
if valuelen > masterlen or (valuelen < masterlen and setitem):
raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format(
self.opt._name, masterp))
@ -427,59 +509,43 @@ class Multi(list):
#else: same len so do nothing
return value
def _valid_master(self, value):
masterlen = len(value)
values = self.context().cfgimpl_get_values()
def _valid_master(self):
#masterlen = len(value)
values = self._getcontext().cfgimpl_get_values()
for slave in self.opt._master_slaves:
path = values._get_opt_path(slave)
if not values._is_default_owner(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)
Multi(values._getvalue(slave, path), self.context, slave, path)
def __setitem__(self, index, value):
self._validate(value, index)
#assume not checking mandatory property
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)
only if the option is a master
"""
context = self._getcontext()
if not force:
if self.opt.impl_get_multitype() == multitypes.slave:
raise SlaveError(_("cannot append a value on a multi option {0}"
" which is a slave").format(self.opt._name))
elif self.opt.impl_get_multitype() == multitypes.master:
values = self.context().cfgimpl_get_values()
if value is None and self.opt.impl_has_callback():
values = context.cfgimpl_get_values()
if value is undefined and self.opt.impl_has_callback():
value = values._getcallback_value(self.opt)
#Force None il return a list
if isinstance(value, list):
value = None
index = self.__len__()
if value is undefined:
value = self.opt.impl_getdefault_multi()
self._validate(value, index)
super(Multi, self).append(value)
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)
if not force and self.opt.impl_get_multitype() == multitypes.master:
for slave in self.opt.impl_get_master_slaves():
path = values._get_opt_path(slave)
@ -488,16 +554,15 @@ class Multi(list):
dvalue = values._getcallback_value(slave, index=index)
else:
dvalue = slave.impl_getdefault_multi()
old_value = values.getitem(slave, path,
old_value = values.getitem(slave, path, validate=False,
validate_properties=False)
if len(old_value) < self.__len__():
values.getitem(slave, path,
validate_properties=False).append(
dvalue, force=True)
else:
values.getitem(slave, path,
validate_properties=False)[
index] = dvalue
if len(old_value) + 1 != self.__len__():
raise SlaveError(_("invalid len for the slave: {0}"
" which has {1} as master").format(
self.opt._name, self.__len__()))
values.getitem(slave, path, validate=False,
validate_properties=False).append(
dvalue, force=True)
def sort(self, cmp=None, key=None, reverse=False):
if self.opt.impl_get_multitype() in [multitypes.slave,
@ -510,7 +575,7 @@ class Multi(list):
super(Multi, self).sort(key=key, reverse=reverse)
else:
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):
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 "
"slave").format(self.opt._name))
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):
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 "
"slave").format(self.opt._name))
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):
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 "
"slave").format(self.opt._name))
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):
if value is not None:
try:
self.opt.impl_validate(value, context=self.context(),
self.opt.impl_validate(value, context=self._getcontext(),
force_index=force_index)
except ValueError as err:
raise ValueError(_("invalid value {0} "
@ -557,19 +622,20 @@ class Multi(list):
:type force: boolean
:returns: item at index
"""
context = self._getcontext()
if not force:
if self.opt.impl_get_multitype() == multitypes.slave:
raise SlaveError(_("cannot pop a value on a multi option {0}"
" 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():
values = self.context().cfgimpl_get_values()
values = context.cfgimpl_get_values()
if not values.is_default_owner(slave):
#get multi without valid properties
values.getitem(slave,
values.getitem(slave, validate=False,
validate_properties=False
).pop(index, force=True)
#set value without valid properties
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

View File

@ -1,63 +1,61 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Project-Id-Version: Tiramisu\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"
"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"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\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 ""
"unable to carry out a calculation, option {0} has properties: {1} for: {2}"
msgstr ""
"impossible d'effectuer le calcul, l'option {0} a les propriétés : {1} pour : "
"{2}"
#: tiramisu/autolib.py:153
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
#: tiramisu/config.py:52
msgid "descr must be an optiondescription, not {0}"
msgstr "descr doit être une optiondescription pas un {0}"
#: tiramisu/config.py:126
#: tiramisu/config.py:127
msgid "unknown group_type: {0}"
msgstr "group_type inconnu: {0}"
#: tiramisu/config.py:162
msgid ""
"no option description found for this config (may be metaconfig without meta)"
msgstr ""
"pas d'option description trouvé pour cette config (peut être une metaconfig "
"sans meta)"
#: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57
#: tiramisu/value.py:485
msgid "the context does not exist anymore"
msgstr "le context n'existe plus"
#: 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"
msgstr "ne peut pas attribuer une valeur à une OptionDescription"
#: tiramisu/config.py:319
#: tiramisu/config.py:325
msgid "unknown type_ type {0}for _find"
msgstr "type_ type {0} pour _find inconnu"
#: tiramisu/config.py:358
#: tiramisu/config.py:364
msgid "no option found in config with these criteria"
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"
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}"
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}"
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"
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"
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"
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}"
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"
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}"
msgstr ""
"une default_multi est renseignée alors que multi est False dans l'option : "
"{0}"
#: tiramisu/option.py:313
#: tiramisu/option.py:312
msgid "invalid default_multi value {0} for 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"
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 ""
"params defined for a callback function but no callback defined yet for "
"option {0}"
@ -107,221 +130,289 @@ msgstr ""
"params définis pour une fonction callback mais par de callback encore "
"définis pour l'option {0}"
#: tiramisu/option.py:360
msgid "option not in all_cons_opts"
msgstr "option non présentante dans all_cons_opts"
#: tiramisu/option.py:425 tiramisu/option.py:450
msgid "invalid value for option {0}: {1}"
msgstr "valeur invalide pour l'option {0} : {1}"
#: tiramisu/option.py:432 tiramisu/value.py:545
msgid "invalid value {0} for option {1}: {2}"
msgstr "valeur invalide {0} pour l'option {1} : {2}"
#: tiramisu/option.py:444
msgid "warning on the value of the option {0}: {1}"
msgstr "avertissement sur la valeur de l'option {0} : {1}"
#: tiramisu/option.py:449
msgid "which must be a list"
msgstr "lequel doit être une liste"
#: tiramisu/option.py:461
msgid "invalid value {0} for option {1} which must be a list"
msgstr "valeur invalide pour l'option {0} : {1} laquelle doit être une liste"
#: tiramisu/option.py:509
msgid "consistency should be set with an option"
#: tiramisu/option.py:519
msgid "consistency must be set with an option"
msgstr "consistency doit être configuré avec une option"
#: tiramisu/option.py:511
#: tiramisu/option.py:521
msgid "cannot add consistency with itself"
msgstr "ne peut ajouter une consistency avec lui même"
#: tiramisu/option.py:513
msgid "every options in consistency should be multi or none"
#: tiramisu/option.py:523
msgid "every options in consistency must be multi or none"
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
msgid "same value for {0} and {1}"
msgstr "même valeur pour {0} et {1}"
#: tiramisu/option.py:544
msgid "same value for {0} and {1}, should be different"
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}"
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}"
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"
msgstr "valeur {0} n'est pas permis, seules {1} sont autorisées"
#: tiramisu/option.py:679
msgid "value must be a boolean"
msgstr "valeur doit être un booléen"
#: tiramisu/option.py:677
msgid "invalid boolean"
msgstr "booléen invalide"
#: tiramisu/option.py:689
msgid "value must be an integer"
msgstr "valeur doit être un nombre entier"
#: tiramisu/option.py:687
msgid "invalid integer"
msgstr "nombre invalide"
#: tiramisu/option.py:699
msgid "value must be a float"
msgstr "valeur doit être un nombre flottant"
#: tiramisu/option.py:697
msgid "invalid float"
msgstr "invalide nombre flottan"
#: tiramisu/option.py:709
msgid "value must be a string, not {0}"
msgstr "valeur doit être une chaîne, pas {0}"
#: tiramisu/option.py:707
msgid "invalid string"
msgstr "invalide caractère"
#: tiramisu/option.py:727
msgid "value must be an unicode"
msgstr "valeur doit être une valeur unicode"
#: tiramisu/option.py:724
msgid "invalid unicode"
msgstr "invalide unicode"
#: tiramisu/option.py:739
#: tiramisu/option.py:736
msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr "symlinkoption mal formé, doit être une option pour symlink {0}"
#: tiramisu/option.py:788
msgid "invalid IP {0}"
msgstr "adresse IP invalide {0}"
#: tiramisu/option.py:787 tiramisu/option.py:790 tiramisu/option.py:795
msgid "invalid IP"
msgstr "adresse IP invalide"
#: tiramisu/option.py:793
msgid "IP mustn't not be in reserved class"
msgstr "IP ne doit pas être d'une classe reservée"
#: tiramisu/option.py:801
msgid "IP shouldn't be in reserved class"
msgstr "l'adresse IP ne devrait pas être d'une classe réservée"
#: tiramisu/option.py:795
msgid "IP must be in private class"
msgstr "IP doit être dans la classe privée"
#: tiramisu/option.py:803
msgid "invalid IP, mustn't be in reserved class"
msgstr "adresse IP invalide, ne doit pas être dans une classe réservée"
#: tiramisu/option.py:833
msgid "inconsistency in allowed range"
msgstr "inconsistence dans la plage autorisée"
#: tiramisu/option.py:807
msgid "IP should be in private class"
msgstr "l'adresse IP devrait être dans une classe privée"
#: tiramisu/option.py:838
msgid "max value is empty"
msgstr "la valeur maximum est vide"
#: tiramisu/option.py:809
msgid "invalid IP, must be in private class"
msgstr "adresse IP invalide, doit être dans la classe privée"
#: tiramisu/option.py:877
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
#: tiramisu/option.py:814 tiramisu/option.py:989
msgid "invalid len for 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 ""
"invalid broadcast {0} ({1}) with network {2} ({3}) and netmask {4} ({5})"
msgstr ""
"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"
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"
msgstr "allow_ip doit être un booléen"
#: tiramisu/option.py:1012
msgid "invalid value for {0}, must have dot"
msgstr "valeur invalide pour {0}, doit avoir un point"
#: tiramisu/option.py:1021
msgid "allow_without_dot must be a boolean"
msgstr "allow_without_dot doit être un booléen"
#: tiramisu/option.py:1015
msgid "invalid domainname's length for {0} (max {1})"
msgstr "longueur du nom de domaine invalide pour {0} (maximum {1})"
#: tiramisu/option.py:1065
msgid "invalid domainname, must have dot"
msgstr "nom de domaine invalide, doit avoir un point"
#: tiramisu/option.py:1018
msgid "invalid domainname's length for {0} (min 2)"
msgstr "longueur du nom de domaine invalide pour {0} (minimum 2)"
#: tiramisu/option.py:1067
msgid "invalid domainname's length (max 255)"
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"
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}"
msgstr "nom de l'option dupliqué : {0}"
#: tiramisu/option.py:1067
#: tiramisu/option.py:1193
msgid "unknown Option {0} in OptionDescription {1}"
msgstr "Option {0} inconnue pour l'OptionDescription {1}"
#: tiramisu/option.py:1118
#: tiramisu/option.py:1244
msgid "duplicate option: {0}"
msgstr "option dupliquée : {0}"
#: tiramisu/option.py:1148
#: tiramisu/option.py:1275
msgid "consistency with option {0} which is not in 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}"
msgstr "pas d'option pour le chemin {0}"
#: tiramisu/option.py:1162
#: tiramisu/option.py:1289
msgid "no option {0} found"
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})"
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"
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"
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"
msgstr ""
"option non autorisée {0} dans le groupe {1} : cette option n'est pas une "
"multi"
#: tiramisu/option.py:1202
#: tiramisu/option.py:1327
msgid "master group with wrong master name for {0}"
msgstr "le groupe maître avec un nom de maître érroné pour {0}"
#: tiramisu/option.py:1211
msgid "no child has same nom has master group for: {0}"
msgstr "pas d'enfant avec le nom du groupe maître pour {0} "
#: tiramisu/option.py:1335
msgid "callback of master's option shall not refered a slave's ones"
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"
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"
msgstr ""
"type requirements malformé pour l'option : {0}, doit être un dictionnaire"
#: tiramisu/option.py:1323
#: tiramisu/option.py:1460
msgid ""
"malformed requirements for option: {0} require must have option, expected "
"and action keys"
@ -329,110 +420,110 @@ msgstr ""
"requirements malformé pour l'option : {0} l'exigence doit avoir les clefs "
"option, expected et action"
#: tiramisu/option.py:1328
#: tiramisu/option.py:1465
msgid "malformed requirements for option: {0} inverse must be boolean"
msgstr ""
"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"
msgstr ""
"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"
msgstr ""
"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}"
msgstr "requirements mal formés doit être une option dans l'option {0}"
#: tiramisu/option.py:1343
msgid "malformed requirements option {0} should not be a multi"
#: tiramisu/option.py:1480
msgid "malformed requirements option {0} must not be a multi"
msgstr "requirements mal formés l'option {0} ne doit pas être une multi"
#: tiramisu/option.py:1349
#: tiramisu/option.py:1486
msgid ""
"malformed requirements second argument must be valid for option {0}: {1}"
msgstr ""
"requirements mal formés deuxième argument doit être valide pour l'option "
"{0} : {1}"
#: tiramisu/option.py:1354
#: tiramisu/option.py:1491
msgid "inconsistency in action types for option: {0} action: {1}"
msgstr "incohérence dans les types action pour l'option : {0} action {1}"
#: tiramisu/option.py:1379
msgid "{0} should be a function"
#: tiramisu/option.py:1516
msgid "{0} must be a function"
msgstr "{0} doit être une fonction"
#: tiramisu/option.py:1382
msgid "{0}_params should be a dict"
msgstr "{0}_params devrait être un dict"
#: tiramisu/option.py:1519
msgid "{0}_params must be a dict"
msgstr "{0}_params doit être un dict"
#: tiramisu/option.py:1385
msgid "{0}_params with key {1} should not have length different to 1"
#: tiramisu/option.py:1522
msgid "{0}_params with key {1} mustn't have length different to 1"
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
msgid "{0}_params should be tuple for key \"{1}\""
msgstr "{0}_params devrait être un tuple pour la clef \"{1}\""
#: tiramisu/option.py:1526
msgid "{0}_params must be tuple for key \"{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"
msgstr "validator n'accepte pas de tuple"
#: tiramisu/option.py:1398
msgid "{0}_params should have an option not a {0} for first argument"
msgstr "{0}_params devrait avoir une option pas un {0} pour premier argument"
#: tiramisu/option.py:1535
msgid "{0}_params must have an option not a {0} for first argument"
msgstr "{0}_params doit avoir une option pas un {0} pour premier argument"
#: tiramisu/option.py:1402
msgid "{0}_params should have a boolean not a {0} for second argument"
msgstr "{0}_params devrait avoir un boolean pas un {0} pour second argument"
#: tiramisu/option.py:1539
msgid "{0}_params must have a boolean not a {0} for 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}"
msgstr "ne peut redéfinir ({0})"
#: tiramisu/setting.py:116
#: tiramisu/setting.py:121
msgid "can't unbind {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"
msgstr ""
"ne peut ajouter la propriété {0} dans l'option {1}: cette propriété est "
"calculée"
#: tiramisu/setting.py:317
#: tiramisu/setting.py:363
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"
#: tiramisu/setting.py:332
#: tiramisu/setting.py:378
msgid "if opt is not None, path should not be None in _getproperties"
msgstr ""
"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"
msgstr ""
"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}"
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"
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}"
msgstr "invalide owner générique {0}"
#: tiramisu/setting.py:553
#: tiramisu/setting.py:606
msgid ""
"malformed requirements imbrication detected for option: '{0}' with "
"requirement on: '{1}'"
@ -440,75 +531,119 @@ msgstr ""
"imbrication de requirements mal formés detectée pour l'option : '{0}' avec "
"requirement sur : '{1}'"
#: tiramisu/setting.py:565
#: tiramisu/setting.py:617
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}"
#: tiramisu/storage/__init__.py:47
#: tiramisu/storage/__init__.py:52
msgid "storage_type is already set, cannot rebind it"
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}"
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"
msgstr ""
"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"
msgstr "session déjà utilisée"
#: tiramisu/storage/dictionary/storage.py:50
#: tiramisu/storage/dictionary/storage.py:52
msgid "a dictionary cannot be persistent"
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}"
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"
msgstr "longueur invalide pour une esclave : {0} qui a {1} comme maître"
#: tiramisu/value.py:438
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
#: tiramisu/value.py:534
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"
#: tiramisu/value.py:505
#: tiramisu/value.py:572
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"
#: tiramisu/value.py:509
#: tiramisu/value.py:576
msgid "cmp is not permitted in python v3 or greater"
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"
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"
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"
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"
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"
#~ msgstr "valeur invalide {0} pour l'option {1} qui doit être une liste"
#~ msgid "option not in all_cons_opts"
#~ 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"
#~ 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"
#~ 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}"
#~ msgstr "group_type non autorisé : {0}"

View File

@ -5,7 +5,7 @@
msgid ""
msgstr ""
"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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -15,43 +15,44 @@ msgstr ""
"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}"
msgstr ""
#: tiramisu/autolib.py:153
msgid "unable to carry out a calculation, option value with multi types must have same length for: {0}"
msgstr ""
#: tiramisu/config.py:51
#: tiramisu/config.py:52
msgid "descr must be an optiondescription, not {0}"
msgstr ""
#: tiramisu/config.py:126
#: tiramisu/config.py:127
msgid "unknown group_type: {0}"
msgstr ""
#: tiramisu/config.py:162
msgid "no option description found for this config (may be metaconfig without meta)"
#: tiramisu/config.py:164 tiramisu/setting.py:339 tiramisu/value.py:57
#: tiramisu/value.py:485
msgid "the context does not exist anymore"
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"
msgstr ""
#: tiramisu/config.py:319
#: tiramisu/config.py:325
msgid "unknown type_ type {0}for _find"
msgstr ""
#: tiramisu/config.py:358
#: tiramisu/config.py:364
msgid "no option found in config with these criteria"
msgstr ""
#: tiramisu/config.py:408
#: tiramisu/config.py:414
msgid "make_dict can't filtering with value without option"
msgstr ""
#: tiramisu/config.py:429
#: tiramisu/config.py:435
msgid "unexpected path {0}, should start with {1}"
msgstr ""
@ -59,411 +60,507 @@ msgstr ""
msgid "opt in getowner must be an option not {0}"
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"
msgstr ""
#: tiramisu/option.py:77
#: tiramisu/option.py:76
msgid "invalid properties type {0} for {1}, must be a tuple"
msgstr ""
#: tiramisu/option.py:115
#: tiramisu/option.py:114
msgid "'{0}' ({1}) object attribute '{2}' is read-only"
msgstr ""
#: tiramisu/option.py:142 tiramisu/value.py:360
#: tiramisu/option.py:141 tiramisu/value.py:395
msgid "information's item not found: {0}"
msgstr ""
#: tiramisu/option.py:204
#: tiramisu/option.py:203
msgid "cannot serialize Option, only in OptionDescription"
msgstr ""
#: tiramisu/option.py:307
#: tiramisu/option.py:306
msgid "a default_multi is set whereas multi is False in option: {0}"
msgstr ""
#: tiramisu/option.py:313
#: tiramisu/option.py:312
msgid "invalid default_multi value {0} for option {1}: {2}"
msgstr ""
#: tiramisu/option.py:318
#: tiramisu/option.py:317
msgid "default value not allowed if option: {0} is calculated"
msgstr ""
#: tiramisu/option.py:321
#: tiramisu/option.py:320
msgid "params defined for a callback function but no callback defined yet for option {0}"
msgstr ""
#: tiramisu/option.py:360
msgid "option not in all_cons_opts"
#: tiramisu/option.py:425 tiramisu/option.py:450
msgid "invalid value for option {0}: {1}"
msgstr ""
#: tiramisu/option.py:432 tiramisu/value.py:545
msgid "invalid value {0} for option {1}: {2}"
#: tiramisu/option.py:444
msgid "warning on the value of the option {0}: {1}"
msgstr ""
#: tiramisu/option.py:449
msgid "which must be a list"
#: tiramisu/option.py:461
msgid "invalid value {0} for option {1} which must be a list"
msgstr ""
#: tiramisu/option.py:509
msgid "consistency should be set with an option"
#: tiramisu/option.py:519
msgid "consistency must be set with an option"
msgstr ""
#: tiramisu/option.py:511
#: tiramisu/option.py:521
msgid "cannot add consistency with itself"
msgstr ""
#: tiramisu/option.py:513
msgid "every options in consistency should be multi or none"
#: tiramisu/option.py:523
msgid "every options in consistency must be multi or none"
msgstr ""
#: tiramisu/option.py:533
msgid "same value for {0} and {1}"
#: tiramisu/option.py:544
msgid "same value for {0} and {1}, should be different"
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}"
msgstr ""
#: tiramisu/option.py:645
#: tiramisu/option.py:643
msgid "open_values must be a boolean for {0}"
msgstr ""
#: tiramisu/option.py:667
#: tiramisu/option.py:665
msgid "value {0} is not permitted, only {1} is allowed"
msgstr ""
#: tiramisu/option.py:679
msgid "value must be a boolean"
#: tiramisu/option.py:677
msgid "invalid boolean"
msgstr ""
#: tiramisu/option.py:689
msgid "value must be an integer"
#: tiramisu/option.py:687
msgid "invalid integer"
msgstr ""
#: tiramisu/option.py:699
msgid "value must be a float"
#: tiramisu/option.py:697
msgid "invalid float"
msgstr ""
#: tiramisu/option.py:709
msgid "value must be a string, not {0}"
#: tiramisu/option.py:707
msgid "invalid string"
msgstr ""
#: tiramisu/option.py:727
msgid "value must be an unicode"
#: tiramisu/option.py:724
msgid "invalid unicode"
msgstr ""
#: tiramisu/option.py:739
#: tiramisu/option.py:736
msgid "malformed symlinkoption must be an option for symlink {0}"
msgstr ""
#: tiramisu/option.py:788
msgid "invalid IP {0}"
#: tiramisu/option.py:787 tiramisu/option.py:790 tiramisu/option.py:795
msgid "invalid IP"
msgstr ""
#: tiramisu/option.py:793
msgid "IP mustn't not be in reserved class"
#: tiramisu/option.py:801
msgid "IP shouldn't be in reserved class"
msgstr ""
#: tiramisu/option.py:795
msgid "IP must be in private class"
#: tiramisu/option.py:803
msgid "invalid IP, mustn't be in reserved class"
msgstr ""
#: tiramisu/option.py:833
msgid "inconsistency in allowed range"
#: tiramisu/option.py:807
msgid "IP should be in private class"
msgstr ""
#: tiramisu/option.py:838
msgid "max value is empty"
#: tiramisu/option.py:809
msgid "invalid IP, must be in private class"
msgstr ""
#: tiramisu/option.py:877
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
#: tiramisu/option.py:814 tiramisu/option.py:989
msgid "invalid len for vals"
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})"
msgstr ""
#: tiramisu/option.py:979
#: tiramisu/option.py:1016
msgid "unknown type_ {0} for hostname"
msgstr ""
#: tiramisu/option.py:982
#: tiramisu/option.py:1019
msgid "allow_ip must be a boolean"
msgstr ""
#: tiramisu/option.py:1012
msgid "invalid value for {0}, must have dot"
#: tiramisu/option.py:1021
msgid "allow_without_dot must be a boolean"
msgstr ""
#: tiramisu/option.py:1015
msgid "invalid domainname's length for {0} (max {1})"
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}"
#: tiramisu/option.py:1065
msgid "invalid domainname, must have dot"
msgstr ""
#: tiramisu/option.py:1067
msgid "unknown Option {0} in OptionDescription {1}"
msgid "invalid domainname's length (max 255)"
msgstr ""
#: tiramisu/option.py:1118
msgid "duplicate option: {0}"
#: tiramisu/option.py:1069
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 ""
#: 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"
msgstr ""
#: tiramisu/option.py:1156
#: tiramisu/option.py:1283
msgid "no option for path {0}"
msgstr ""
#: tiramisu/option.py:1162
#: tiramisu/option.py:1289
msgid "no option {0} found"
msgstr ""
#: tiramisu/option.py:1172
#: tiramisu/option.py:1299
msgid "cannot change group_type if already set (old {0}, new {1})"
msgstr ""
#: tiramisu/option.py:1185
#: tiramisu/option.py:1311
msgid "master group {0} shall not have a subgroup"
msgstr ""
#: tiramisu/option.py:1188
#: tiramisu/option.py:1314
msgid "master group {0} shall not have a symlinkoption"
msgstr ""
#: tiramisu/option.py:1191
#: tiramisu/option.py:1317
msgid "not allowed option {0} in group {1}: this option is not a multi"
msgstr ""
#: tiramisu/option.py:1202
#: tiramisu/option.py:1327
msgid "master group with wrong master name for {0}"
msgstr ""
#: tiramisu/option.py:1211
msgid "no child has same nom has master group for: {0}"
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}"
#: tiramisu/option.py:1335
msgid "callback of master's option shall not refered a slave's ones"
msgstr ""
#: tiramisu/option.py:1343
msgid "malformed requirements option {0} should not be a multi"
msgid "group_type: {0} not allowed"
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}"
msgstr ""
#: tiramisu/option.py:1354
#: tiramisu/option.py:1491
msgid "inconsistency in action types for option: {0} action: {1}"
msgstr ""
#: tiramisu/option.py:1379
msgid "{0} should be a function"
#: tiramisu/option.py:1516
msgid "{0} must be a function"
msgstr ""
#: tiramisu/option.py:1382
msgid "{0}_params should be a dict"
#: tiramisu/option.py:1519
msgid "{0}_params must be a dict"
msgstr ""
#: tiramisu/option.py:1385
msgid "{0}_params with key {1} should not have length different to 1"
#: tiramisu/option.py:1522
msgid "{0}_params with key {1} mustn't have length different to 1"
msgstr ""
#: tiramisu/option.py:1389
msgid "{0}_params should be tuple for key \"{1}\""
#: tiramisu/option.py:1526
msgid "{0}_params must be tuple for key \"{1}\""
msgstr ""
#: tiramisu/option.py:1395
#: tiramisu/option.py:1532
msgid "validator not support tuple"
msgstr ""
#: tiramisu/option.py:1398
msgid "{0}_params should have an option not a {0} for first argument"
#: tiramisu/option.py:1535
msgid "{0}_params must have an option not a {0} for first argument"
msgstr ""
#: tiramisu/option.py:1402
msgid "{0}_params should have a boolean not a {0} for second argument"
msgstr ""
#: tiramisu/setting.py:111
msgid "can't rebind {0}"
#: tiramisu/option.py:1539
msgid "{0}_params must have a boolean not a {0} for second argument"
msgstr ""
#: tiramisu/setting.py:116
msgid "can't rebind {0}"
msgstr ""
#: tiramisu/setting.py:121
msgid "can't unbind {0}"
msgstr ""
#: tiramisu/setting.py:254
#: tiramisu/setting.py:272
msgid "cannot append {0} property for option {1}: this property is calculated"
msgstr ""
#: tiramisu/setting.py:317
#: tiramisu/setting.py:363
msgid "opt and all_properties must not be set together in reset"
msgstr ""
#: tiramisu/setting.py:332
#: tiramisu/setting.py:378
msgid "if opt is not None, path should not be None in _getproperties"
msgstr ""
#: tiramisu/setting.py:435
#: tiramisu/setting.py:487
msgid "cannot change the value for option {0} this option is frozen"
msgstr ""
#: tiramisu/setting.py:441
#: tiramisu/setting.py:493
msgid "trying to access to an option named: {0} with properties {1}"
msgstr ""
#: tiramisu/setting.py:459
#: tiramisu/setting.py:511
msgid "permissive must be a tuple"
msgstr ""
#: tiramisu/setting.py:466 tiramisu/value.py:299
#: tiramisu/setting.py:518 tiramisu/value.py:334
msgid "invalid generic owner {0}"
msgstr ""
#: tiramisu/setting.py:553
#: tiramisu/setting.py:606
msgid "malformed requirements imbrication detected for option: '{0}' with requirement on: '{1}'"
msgstr ""
#: tiramisu/setting.py:565
#: tiramisu/setting.py:617
msgid "option '{0}' has requirement's property error: {1} {2}"
msgstr ""
#: tiramisu/storage/__init__.py:47
#: tiramisu/storage/__init__.py:52
msgid "storage_type is already set, cannot rebind it"
msgstr ""
#: tiramisu/storage/__init__.py:81
#: tiramisu/storage/__init__.py:86
msgid "option {0} not already exists in storage {1}"
msgstr ""
#: tiramisu/storage/dictionary/storage.py:37
#: tiramisu/storage/dictionary/storage.py:39
msgid "dictionary storage cannot delete session"
msgstr ""
#: tiramisu/storage/dictionary/storage.py:48
#: tiramisu/storage/dictionary/storage.py:50
msgid "session already used"
msgstr ""
#: tiramisu/storage/dictionary/storage.py:50
#: tiramisu/storage/dictionary/storage.py:52
msgid "a dictionary cannot be persistent"
msgstr ""
#: tiramisu/value.py:306
#: tiramisu/value.py:341
msgid "no value for {0} cannot change owner to {1}"
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"
msgstr ""
#: tiramisu/value.py:438
msgid "invalid len for the master: {0} which has {1} as slave with greater len"
msgstr ""
#: tiramisu/value.py:468
#: tiramisu/value.py:534
msgid "cannot append a value on a multi option {0} which is a slave"
msgstr ""
#: tiramisu/value.py:505
#: tiramisu/value.py:572
msgid "cannot sort multi option {0} if master or slave"
msgstr ""
#: tiramisu/value.py:509
#: tiramisu/value.py:576
msgid "cmp is not permitted in python v3 or greater"
msgstr ""
#: tiramisu/value.py:518
#: tiramisu/value.py:585
msgid "cannot reverse multi option {0} if master or slave"
msgstr ""
#: tiramisu/value.py:526
#: tiramisu/value.py:593
msgid "cannot insert multi option {0} if master or slave"
msgstr ""
#: tiramisu/value.py:534
#: tiramisu/value.py:601
msgid "cannot extend multi option {0} if master or slave"
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"
msgstr ""