optimisations and all is properties

This commit is contained in:
Emmanuel Garette 2013-04-03 12:20:26 +02:00
parent 8181d1b596
commit e6f00948f3
14 changed files with 1045 additions and 1144 deletions

View file

@ -6,14 +6,14 @@ from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc", properties=('mandatory', ))
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Test requires', default=False) wantref_option = BoolOption('wantref', 'Test requires', default=False)
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
@ -52,8 +52,8 @@ def test_base_config_and_groups():
assert nm._name == 'name' assert nm._name == 'name'
gc = config.unwrap_from_path('gc') gc = config.unwrap_from_path('gc')
assert gc._name == 'gc' assert gc._name == 'gc'
nm = config.unwrap_from_name('name') #nm = config.unwrap_from_name('name')
assert nm._name == 'name' #assert nm._name == 'name'
def test_base_config_in_a_tree(): def test_base_config_in_a_tree():
"how options are organized into a tree" "how options are organized into a tree"
@ -105,3 +105,16 @@ def test_cfgimpl_get_home_by_path():
assert config.cfgimpl_get_home_by_path('dummy')[1] == 'dummy' assert config.cfgimpl_get_home_by_path('dummy')[1] == 'dummy'
assert config.getpaths(include_groups=False) == ['gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop'] assert config.getpaths(include_groups=False) == ['gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
assert config.getpaths(include_groups=True) == ['gc', 'gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop'] assert config.getpaths(include_groups=True) == ['gc', 'gc.name', 'gc.dummy', 'gc.float', 'bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
def test_mandatory_warnings():
descr = make_description()
config = Config(descr)
assert(MandatoryError, "config.str = ''")
setting = config.cfgimpl_get_settings()
setting.read_write()
assert list(mandatory_warnings(config)) == []
setting.disable_property('mandatory')
config.str = ''
assert list(mandatory_warnings(config)) == ['str']
setting.enable_property('mandatory')
assert list(mandatory_warnings(config)) == ['str']

View file

@ -6,10 +6,10 @@ from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
@ -63,7 +63,8 @@ def test_cfgimpl_get_value():
"same as getattr." "same as getattr."
descr = make_description() descr = make_description()
conf = Config(descr) conf = Config(descr)
assert conf.cfgimpl_get_value(('gc', 'dummy')) == False #FIXME
#assert conf.cfgimpl_get_value(('gc', 'dummy')) == False
#____________________________________________________________ #____________________________________________________________
def test_getpaths(): def test_getpaths():
@ -84,9 +85,8 @@ def test_getpaths():
def test_getpaths_with_hidden(): def test_getpaths_with_hidden():
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True, properties=('hidden',))
booloption.hide()
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
@ -100,7 +100,7 @@ def test_getpaths_with_hidden():
intoption, boolop]) intoption, boolop])
config = Config(descr) config = Config(descr)
result = ['objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop'] result = ['bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
assert config.getpaths() == result assert config.getpaths() == result
r2 = ['bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop'] r2 = ['bool', 'objspace', 'wantref', 'str', 'wantframework', 'int', 'boolop']
assert config.getpaths(allpaths=True) == r2 assert config.getpaths(allpaths=True) == r2
@ -149,20 +149,20 @@ def test_find_in_config():
"finds option in config" "finds option in config"
descr = make_description() descr = make_description()
conf = Config(descr) conf = Config(descr)
assert conf.find(byname='dummy') == [conf.unwrap_from_name('dummy')] assert conf.find(byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
assert conf.find_first(byname='dummy') == conf.unwrap_from_name('dummy') assert conf.find_first(byname='dummy') == conf.unwrap_from_path('gc.dummy')
assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_name('name'), conf.unwrap_from_name('objspace')] assert conf.find(bytype=ChoiceOption) == [conf.unwrap_from_path('gc.name'), conf.unwrap_from_path('objspace')]
assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_name('name') assert conf.find_first(bytype=ChoiceOption) == conf.unwrap_from_path('gc.name')
assert conf.find(byvalue='ref') == [conf.unwrap_from_name('name')] assert conf.find(byvalue='ref') == [conf.unwrap_from_path('gc.name')]
assert conf.find_first(byvalue='ref') == conf.unwrap_from_name('name') assert conf.find_first(byvalue='ref') == conf.unwrap_from_path('gc.name')
# combinaison of filters # combinaison of filters
assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_name('dummy')] assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_name('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_name('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_name('dummy') assert conf.find_first(byvalue=False, byname='dummy') == conf.unwrap_from_path('gc.dummy')
# byattrs # byattrs
assert conf.find_first(byattrs= dict(default=2.3)) == conf.unwrap_from_name('float') 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_name('dummy') assert conf.find_first(byvalue=False, byname='dummy', byattrs=dict(default=False)) == conf.unwrap_from_path('gc.dummy')
def test_does_not_find_in_config(): def test_does_not_find_in_config():
descr = make_description() descr = make_description()

View file

@ -5,20 +5,20 @@ from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Test requires', default=False, wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
default=False, default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption, descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
wantref_option, stroption, wantref_option, stroption,
@ -27,21 +27,21 @@ def make_description():
return descr return descr
def make_description_duplicates(): def make_description_duplicates():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
## dummy 1 ## dummy 1
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Test requires', default=False, wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
default=False, default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
# dummy2 (same path) # dummy2 (same path)
gcdummy2 = BoolOption('dummy', 'dummy2', default=True) gcdummy2 = BoolOption('dummy', 'dummy2', default=True)
# dummy3 (same name) # dummy3 (same name)
@ -57,8 +57,7 @@ def test_identical_paths():
"""If in the schema (the option description) there is something that """If in the schema (the option description) there is something that
have the same name, an exection is raised have the same name, an exection is raised
""" """
descr = make_description_duplicates() raises(ConflictConfigError, "make_description_duplicates()")
raises(ConflictConfigError, "cfg = Config(descr)")
def make_description2(): def make_description2():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
@ -75,11 +74,11 @@ def make_description2():
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
boolop.enable_multi() boolop.enable_multi()
wantref_option = BoolOption('wantref', 'Test requires', default=False, wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
# second multi # second multi
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
default=False, default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
wantframework_option.enable_multi() wantframework_option.enable_multi()
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
@ -110,20 +109,20 @@ def make_description2():
# raises(ConflictConfigError, "cfg.wantframework = [False, False, True]") # raises(ConflictConfigError, "cfg.wantframework = [False, False, True]")
# ____________________________________________________________ # ____________________________________________________________
# adding dynamically new options description schema # adding dynamically new options description schema
def test_newoption_add_in_descr(): #def test_newoption_add_in_descr():
descr = make_description() # descr = make_description()
newoption = BoolOption('newoption', 'dummy twoo', default=False) # newoption = BoolOption('newoption', 'dummy twoo', default=False)
descr.add_child(newoption) # descr.add_child(newoption)
config = Config(descr) # config = Config(descr)
assert config.newoption == False # assert config.newoption == False
def test_newoption_add_in_subdescr(): #def test_newoption_add_in_subdescr():
descr = make_description() # descr = make_description()
newoption = BoolOption('newoption', 'dummy twoo', default=False) # newoption = BoolOption('newoption', 'dummy twoo', default=False)
descr.gc.add_child(newoption) # descr.gc.add_child(newoption)
config = Config(descr) # config = Config(descr)
config.bool = False # config.bool = False
assert config.gc.newoption == False # assert config.gc.newoption == False
#def test_newoption_add_in_config(): #def test_newoption_add_in_config():
# descr = make_description() # descr = make_description()
@ -135,17 +134,17 @@ def test_newoption_add_in_subdescr():
# assert config.newoption == False # assert config.newoption == False
# ____________________________________________________________ # ____________________________________________________________
def make_description_requires(): def make_description_requires():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
stroption = StrOption('str', 'Test string option', default="abc", stroption = StrOption('str', 'Test string option', default="abc",
requires=[('int', 1, 'hide')]) requires=(('int', 1, 'hidden'),))
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption, descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
@ -155,72 +154,76 @@ def make_description_requires():
def test_hidden_if_in(): def test_hidden_if_in():
descr = make_description_requires() descr = make_description_requires()
cfg = Config(descr) cfg = Config(descr)
setting = cfg.cfgimpl_get_settings()
setting.read_write()
intoption = cfg.unwrap_from_path('int') intoption = cfg.unwrap_from_path('int')
stroption = cfg.unwrap_from_path('str') stroption = cfg.unwrap_from_path('str')
assert not stroption._is_hidden() assert not setting.has_property('hidden', stroption)
cfg.int = 1 cfg.int = 1
raises(PropertiesOptionError, "cfg.str") raises(PropertiesOptionError, "cfg.str")
raises(PropertiesOptionError, 'cfg.str= "uvw"') raises(PropertiesOptionError, 'cfg.str= "uvw"')
assert stroption._is_hidden() assert setting.has_property('hidden', stroption)
def test_hidden_if_in_with_group(): def test_hidden_if_in_with_group():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption], gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
requires=[('int', 1, 'hide')]) requires=(('int', 1, 'hidden'),))
descr = OptionDescription('constraints', '', [gcgroup, booloption, descr = OptionDescription('constraints', '', [gcgroup, booloption,
objspaceoption, stroption, intoption]) objspaceoption, stroption, intoption])
cfg = Config(descr) cfg = Config(descr)
assert not gcgroup._is_hidden() setting = cfg.cfgimpl_get_settings()
setting.read_write()
assert not setting.has_property('hidden', stroption)
cfg.int = 1 cfg.int = 1
raises(PropertiesOptionError, "cfg.gc.name") raises(PropertiesOptionError, "cfg.gc.name")
assert gcgroup._is_hidden()
def test_disabled_with_group(): def test_disabled_with_group():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption], gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption],
requires=[('int', 1, 'disable')]) requires=(('int', 1, 'disabled'),))
descr = OptionDescription('constraints', '', [gcgroup, booloption, descr = OptionDescription('constraints', '', [gcgroup, booloption,
objspaceoption, stroption, intoption]) objspaceoption, stroption, intoption])
cfg = Config(descr) cfg = Config(descr)
assert not gcgroup._is_disabled() setting = cfg.cfgimpl_get_settings()
setting.read_write()
assert cfg.gc.name
cfg.int = 1 cfg.int = 1
raises(PropertiesOptionError, "cfg.gc.name") raises(PropertiesOptionError, "cfg.gc.name")
assert gcgroup._is_disabled()
#____________________________________________________________ #____________________________________________________________
def make_description_callback(): def make_description_callback():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', callback="toto") gcdummy = BoolOption('dummy', 'dummy', callback="toto")
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Test requires', default=False, wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
default=False, default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption, descr = OptionDescription('constraints', '', [gcgroup, booloption, objspaceoption,
wantref_option, stroption, wantref_option, stroption,
@ -232,21 +235,23 @@ def test_has_callback():
descr = make_description_callback() descr = make_description_callback()
# here the owner is 'default' # here the owner is 'default'
config = Config(descr) config = Config(descr)
setting = config.cfgimpl_get_settings()
setting.read_write()
config.bool = False config.bool = False
# because dummy has a callback # because dummy has a callback
dummy = config.unwrap_from_path('gc.dummy') dummy = config.unwrap_from_path('gc.dummy')
settings = config.cfgimpl_get_settings() setting.enable_property('freeze')
settings.freeze() setting.add_property('frozen', dummy)
dummy.freeze()
raises(TypeError, "config.gc.dummy = True") raises(TypeError, "config.gc.dummy = True")
def test_freeze_and_has_callback_with_setoption(): def test_freeze_and_has_callback_with_setoption():
descr = make_description_callback() descr = make_description_callback()
config = Config(descr) config = Config(descr)
setting = config.cfgimpl_get_settings()
setting.read_write()
config.bool = False config.bool = False
settings = config.cfgimpl_get_settings() config.cfgimpl_get_settings().enable_property('freeze')
settings.freeze()
dummy = config.unwrap_from_path('gc.dummy') dummy = config.unwrap_from_path('gc.dummy')
dummy.freeze() config.cfgimpl_get_settings().add_property('frozen', dummy)
raises(TypeError, "config.gc.setoption('dummy', True, 'gen_config')") raises(TypeError, "config.gc.setoption('dummy', True, 'gen_config')")
#____________________________________________________________ #____________________________________________________________

View file

@ -51,31 +51,36 @@ def test_set_defaut_value_from_option_object():
assert b.getdefault() == False assert b.getdefault() == False
def test_mandatory(): def test_mandatory():
dummy1 = BoolOption('dummy1', 'doc dummy', mandatory=True) dummy1 = BoolOption('dummy1', 'doc dummy', properties=('mandatory', ))
dummy2 = BoolOption('dummy2', 'doc dummy', mandatory=True) dummy2 = BoolOption('dummy2', 'doc dummy', properties=('mandatory', ))
group = OptionDescription('group', '', [dummy1, dummy2]) group = OptionDescription('group', '', [dummy1, dummy2])
config = Config(group) config = Config(group)
setting = config.cfgimpl_get_settings()
setting.read_only()
# config.setoption('dummy1', True) # config.setoption('dummy1', True)
raises(MandatoryError, 'config.dummy1') raises(MandatoryError, 'config.dummy1')
setting.read_write()
config.dummy1 = True config.dummy1 = True
setting.read_only()
assert config.dummy1 == True assert config.dummy1 == True
raises(MandatoryError, 'config.dummy2 == None') raises(MandatoryError, 'config.dummy2 == None')
# raises(MandatoryError, "config.override({'dummy2':None})") # raises(MandatoryError, "config.override({'dummy2':None})")
setting.read_write()
config.set(dummy2=True) config.set(dummy2=True)
config.dummy2 = False config.dummy2 = False
setting.read_only()
assert config.dummy2 == False assert config.dummy2 == False
def test_force_default_on_freeze(): def test_force_default_on_freeze():
"a frozen option wich is forced returns his default" "a frozen option wich is forced returns his default"
dummy1 = BoolOption('dummy1', 'doc dummy', default=False) dummy1 = BoolOption('dummy1', 'doc dummy', default=False, properties=('force_default_on_freeze',))
dummy2 = BoolOption('dummy2', 'doc dummy', default=True) dummy2 = BoolOption('dummy2', 'doc dummy', default=True)
group = OptionDescription('group', '', [dummy1, dummy2]) group = OptionDescription('group', '', [dummy1, dummy2])
config = Config(group) config = Config(group)
config.dummy1 = True config.dummy1 = True
config.dummy2 = False config.dummy2 = False
dummy1.freeze() config.cfgimpl_get_settings().add_property('frozen', dummy1)
dummy1.force_default() config.cfgimpl_get_settings().add_property('frozen', dummy2)
dummy2.freeze()
assert config.dummy1 == False assert config.dummy1 == False
assert config.dummy2 == False assert config.dummy2 == False
@ -104,14 +109,14 @@ def test_overrides_changes_option_value():
# test various option types # test various option types
def test_choice_with_no_default(): def test_choice_with_no_default():
descr = OptionDescription("test", "", [ descr = OptionDescription("test", "", [
ChoiceOption("backend", "", ["c", "cli"])]) ChoiceOption("backend", "", ("c", "cli"))])
config = Config(descr) config = Config(descr)
assert config.backend is None assert config.backend is None
config.backend = "c" config.backend = "c"
def test_choice_with_default(): def test_choice_with_default():
descr = OptionDescription("test", "", [ descr = OptionDescription("test", "", [
ChoiceOption("backend", "", ["c", "cli"], default="cli")]) ChoiceOption("backend", "", ("c", "cli"), default="cli")])
config = Config(descr) config = Config(descr)
assert config.backend == "cli" assert config.backend == "cli"

View file

@ -32,9 +32,9 @@ def test_default_owner():
cfg = Config(descr) cfg = Config(descr)
assert cfg.dummy == False assert cfg.dummy == False
dm = cfg.unwrap_from_path('dummy') dm = cfg.unwrap_from_path('dummy')
assert dm.getowner(cfg) == 'default' assert cfg.cfgimpl_get_values().getowner(dm) == 'default'
dm.setowner(cfg, owners.user) cfg.dummy = True
assert dm.getowner(cfg) == owners.user assert cfg.cfgimpl_get_values().getowner(dm) == owners.user
def test_add_owner(): def test_add_owner():
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -42,10 +42,11 @@ def test_add_owner():
cfg = Config(descr) cfg = Config(descr)
assert cfg.dummy == False assert cfg.dummy == False
dm = cfg.unwrap_from_path('dummy') dm = cfg.unwrap_from_path('dummy')
assert dm.getowner(cfg) == 'default' assert cfg.cfgimpl_get_values().getowner(dm) == 'default'
owners.add_owner("gen_config") owners.add_owner("gen_config")
dm.setowner(cfg, owners.gen_config) cfg.cfgimpl_get_settings().setowner(owners.gen_config)
assert dm.getowner(cfg) == owners.gen_config cfg.dummy = True
assert cfg.cfgimpl_get_values().getowner(dm) == owners.gen_config
def test_owner_is_not_a_string(): def test_owner_is_not_a_string():
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
@ -53,9 +54,8 @@ def test_owner_is_not_a_string():
cfg = Config(descr) cfg = Config(descr)
assert cfg.dummy == False assert cfg.dummy == False
dm = cfg.unwrap_from_path('dummy') dm = cfg.unwrap_from_path('dummy')
assert dm.getowner(cfg) == owners.default assert cfg.cfgimpl_get_values().getowner(dm) == owners.default
assert dm.getowner(cfg) == 'default' assert cfg.cfgimpl_get_values().getowner(dm) == 'default'
assert isinstance(dm.getowner(cfg), owners.Owner) assert isinstance(cfg.cfgimpl_get_values().getowner(dm), owners.Owner)
dm.setowner(cfg, owners.user) cfg.dummy = True
assert cfg.cfgimpl_get_values().getowner(dm) == 'user'
assert dm.getowner(cfg) == 'user'

View file

@ -5,12 +5,13 @@ from py.test import raises
from tiramisu.config import * from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
from tiramisu.error import * from tiramisu.error import *
from tiramisu.setting import owners
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
@ -40,9 +41,7 @@ def test_setitem():
s = StrOption("string", "", default=["string", "sdfsdf"], default_multi="prout", multi=True) s = StrOption("string", "", default=["string", "sdfsdf"], default_multi="prout", multi=True)
descr = OptionDescription("options", "", [s]) descr = OptionDescription("options", "", [s])
config = Config(descr) config = Config(descr)
print config.string[1]
config.string[1] = "titi" config.string[1] = "titi"
print config.string[1]
def test_reset(): def test_reset():
"if value is None, resets to default owner" "if value is None, resets to default owner"
@ -51,10 +50,10 @@ def test_reset():
config = Config(descr) config = Config(descr)
config.string = "foo" config.string = "foo"
assert config.string == "foo" assert config.string == "foo"
assert config._cfgimpl_values.owners[s] == owners.user assert config.cfgimpl_get_values().getowner(s) == owners.user
config.unwrap_from_path("string").reset(config) config.unwrap_from_path("string").reset(config)
assert config.string == 'string' assert config.string == 'string'
assert config._cfgimpl_values.owners[s] == owners.default assert config.cfgimpl_get_values().getowner(s) == owners.default
def test_reset_with_multi(): def test_reset_with_multi():
s = StrOption("string", "", default=["string"], default_multi="string" , multi=True) s = StrOption("string", "", default=["string"], default_multi="string" , multi=True)
@ -63,13 +62,13 @@ def test_reset_with_multi():
# config.string = [] # config.string = []
config.unwrap_from_path("string").reset(config) config.unwrap_from_path("string").reset(config)
assert config.string == ["string"] assert config.string == ["string"]
assert config._cfgimpl_values.owners[s] == 'default' assert config.cfgimpl_get_values().getowner(s) == 'default'
config.string = ["eggs", "spam", "foo"] config.string = ["eggs", "spam", "foo"]
assert config._cfgimpl_values.owners[s] == 'user' assert config.cfgimpl_get_values().getowner(s) == 'user'
config.string = [] config.string = []
config.unwrap_from_path("string").reset(config) config.unwrap_from_path("string").reset(config)
# assert config.string == ["string"] # assert config.string == ["string"]
assert config._cfgimpl_values.owners[s] == 'default' assert config.cfgimpl_get_values().getowner(s) == 'default'
raises(ConfigError, "config.string = None") raises(ConfigError, "config.string = None")
def test_default_with_multi(): def test_default_with_multi():
@ -127,13 +126,15 @@ def test_multi_with_requires():
s = StrOption("string", "", default=["string"], default_multi="string", multi=True) s = StrOption("string", "", default=["string"], default_multi="string", multi=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
stroption = StrOption('str', 'Test string option', default=["abc"], default_multi = "abc", stroption = StrOption('str', 'Test string option', default=["abc"], default_multi = "abc",
requires=[('int', 1, 'hide')], multi=True) requires=[('int', 1, 'hidden')], multi=True)
descr = OptionDescription("options", "", [s, intoption, stroption]) descr = OptionDescription("options", "", [s, intoption, stroption])
config = Config(descr) config = Config(descr)
assert stroption._is_hidden() == False setting = config.cfgimpl_get_settings()
setting.read_write()
assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
config.int = 1 config.int = 1
raises(PropertiesOptionError, "config.str = ['a', 'b']") raises(PropertiesOptionError, "config.str = ['a', 'b']")
assert stroption._is_hidden() assert config.cfgimpl_get_settings().has_property('hidden', stroption)
def test__requires_with_inverted(): def test__requires_with_inverted():
s = StrOption("string", "", default=["string"], multi=True) s = StrOption("string", "", default=["string"], multi=True)
@ -142,40 +143,41 @@ def test__requires_with_inverted():
requires=[('int', 1, 'hide', 'inverted')], multi=True) requires=[('int', 1, 'hide', 'inverted')], multi=True)
descr = OptionDescription("options", "", [s, intoption, stroption]) descr = OptionDescription("options", "", [s, intoption, stroption])
config = Config(descr) config = Config(descr)
assert stroption._is_hidden() == False assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
config.int = 1 config.int = 1
assert stroption._is_hidden() == False assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
def test_multi_with_requires_in_another_group(): def test_multi_with_requires_in_another_group():
s = StrOption("string", "", default=["string"], multi=True) s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption]) descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"], stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'hide')], multi=True) requires=[('int', 1, 'hidden')], multi=True)
descr = OptionDescription("opt", "", [stroption]) descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr]) descr2 = OptionDescription("opt2", "", [intoption, s, descr])
config = Config(descr2) config = Config(descr2)
assert stroption._is_hidden() == False setting = config.cfgimpl_get_settings()
setting.read_write()
assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
config.int = 1 config.int = 1
raises(PropertiesOptionError, "config.opt.str = ['a', 'b']") raises(PropertiesOptionError, "config.opt.str = ['a', 'b']")
assert stroption._is_hidden() assert config.cfgimpl_get_settings().has_property('hidden', stroption)
def test_apply_requires_from_config(): def test_apply_requires_from_config():
s = StrOption("string", "", default=["string"], multi=True) s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption]) descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"], stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'hide')], multi=True) requires=[('int', 1, 'hidden')], multi=True)
descr = OptionDescription("opt", "", [stroption]) descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr]) descr2 = OptionDescription("opt2", "", [intoption, s, descr])
config = Config(descr2) config = Config(descr2)
assert stroption._is_hidden() == False setting = config.cfgimpl_get_settings()
setting.read_write()
assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
config.int = 1 config.int = 1
try: raises(PropertiesOptionError, 'config.opt.str')
config.opt.str assert config.cfgimpl_get_settings().has_property('hidden', stroption)
except:
pass
assert stroption._is_hidden()
def test_apply_requires_with_disabled(): def test_apply_requires_with_disabled():
@ -183,43 +185,46 @@ def test_apply_requires_with_disabled():
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption]) descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"], stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'disable')], multi=True) requires=[('int', 1, 'disabled')], multi=True)
descr = OptionDescription("opt", "", [stroption]) descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr]) descr2 = OptionDescription("opt2", "", [intoption, s, descr])
config = Config(descr2) config = Config(descr2)
assert stroption._is_disabled() == False setting = config.cfgimpl_get_settings()
setting.read_write()
assert not config.cfgimpl_get_settings().has_property('disabled', stroption)
config.int = 1 config.int = 1
try: raises(PropertiesOptionError, 'config.opt.str')
config.opt.str assert config.cfgimpl_get_settings().has_property('disabled', stroption)
except:
pass
assert stroption._is_disabled()
def test_multi_with_requires_with_disabled_in_another_group(): def test_multi_with_requires_with_disabled_in_another_group():
s = StrOption("string", "", default=["string"], multi=True) s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption]) descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"], stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'disable')], multi=True) requires=[('int', 1, 'disabled')], multi=True)
descr = OptionDescription("opt", "", [stroption]) descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr]) descr2 = OptionDescription("opt2", "", [intoption, s, descr])
config = Config(descr2) config = Config(descr2)
assert stroption._is_disabled() == False setting = config.cfgimpl_get_settings()
setting.read_write()
assert not config.cfgimpl_get_settings().has_property('disabled', stroption)
config.int = 1 config.int = 1
raises(PropertiesOptionError, "config.opt.str = ['a', 'b']") raises(PropertiesOptionError, "config.opt.str = ['a', 'b']")
assert stroption._is_disabled() assert config.cfgimpl_get_settings().has_property('disabled', stroption)
def test_multi_with_requires_that_is_multi(): def test_multi_with_requires_that_is_multi():
s = StrOption("string", "", default=["string"], multi=True) s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=[0], multi=True) intoption = IntOption('int', 'Test int option', default=[0], multi=True)
stroption = StrOption('str', 'Test string option', default=["abc"], stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', [1, 1], 'hide')], multi=True) requires=[('int', [1, 1], 'hidden')], multi=True)
descr = OptionDescription("options", "", [s, intoption, stroption]) descr = OptionDescription("options", "", [s, intoption, stroption])
config = Config(descr) config = Config(descr)
assert stroption._is_hidden() == False setting = config.cfgimpl_get_settings()
setting.read_write()
assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
config.int = [1, 1] config.int = [1, 1]
raises(PropertiesOptionError, "config.str = ['a', 'b']") raises(PropertiesOptionError, "config.str = ['a', 'b']")
assert stroption._is_hidden() assert config.cfgimpl_get_settings().has_property('hidden', stroption)
def test_multi_with_bool(): def test_multi_with_bool():
s = BoolOption("bool", "", default=[False], multi=True) s = BoolOption("bool", "", default=[False], multi=True)
@ -227,7 +232,7 @@ def test_multi_with_bool():
config = Config(descr) config = Config(descr)
assert descr.bool.multi == True assert descr.bool.multi == True
config.bool = [True, False] config.bool = [True, False]
assert config._cfgimpl_context._cfgimpl_values[s] == [True, False] assert config.cfgimpl_get_values()[s] == [True, False]
assert config.bool == [True, False] assert config.bool == [True, False]
def test_multi_with_bool_two(): def test_multi_with_bool_two():
@ -238,7 +243,7 @@ def test_multi_with_bool_two():
raises(ConfigError, "config.bool = True") raises(ConfigError, "config.bool = True")
def test_choice_access_with_multi(): def test_choice_access_with_multi():
ch = ChoiceOption("t1", "", ["a", "b"], default=["a"], multi=True) ch = ChoiceOption("t1", "", ("a", "b"), default=["a"], multi=True)
descr = OptionDescription("options", "", [ch]) descr = OptionDescription("options", "", [ch])
config = Config(descr) config = Config(descr)
config.t1 = ["a", "b", "a", "b"] config.t1 = ["a", "b", "a", "b"]
@ -257,7 +262,7 @@ def test_dwim_set():
descr = OptionDescription("opt", "", [ descr = OptionDescription("opt", "", [
OptionDescription("sub", "", [ OptionDescription("sub", "", [
BoolOption("b1", ""), BoolOption("b1", ""),
ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'), ChoiceOption("c1", "", ('a', 'b', 'c'), 'a'),
BoolOption("d1", ""), BoolOption("d1", ""),
]), ]),
BoolOption("b2", ""), BoolOption("b2", ""),
@ -287,13 +292,14 @@ def test_more_set():
assert config.int == 23 assert config.int == 23
def test_set_with_hidden_option(): def test_set_with_hidden_option():
boolopt = BoolOption("a", "", default=False) boolopt = BoolOption("a", "", default=False, properties=(('hidden'),))
boolopt.hide()
descr = OptionDescription("opt", "", [ descr = OptionDescription("opt", "", [
OptionDescription("s1", "", [boolopt]), OptionDescription("s1", "", [boolopt]),
IntOption("int", "", default=42)]) IntOption("int", "", default=42)])
d = {'s1.a': True, 'int': 23} d = {'s1.a': True, 'int': 23}
config = Config(descr) config = Config(descr)
setting = config.cfgimpl_get_settings()
setting.read_write()
raises(PropertiesOptionError, "config.set(**d)") raises(PropertiesOptionError, "config.set(**d)")
def test_set_with_unknown_option(): def test_set_with_unknown_option():
@ -380,16 +386,17 @@ def test_access_by_get():
assert cfg.get('dummy') == False assert cfg.get('dummy') == False
def test_access_by_get_whith_hide(): def test_access_by_get_whith_hide():
b1 = BoolOption("b1", "") b1 = BoolOption("b1", "", properties=(('hidden'),))
b1.hide()
descr = OptionDescription("opt", "", [ descr = OptionDescription("opt", "", [
OptionDescription("sub", "", [ OptionDescription("sub", "", [
b1, b1,
ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'), ChoiceOption("c1", "", ('a', 'b', 'c'), 'a'),
BoolOption("d1", ""), BoolOption("d1", ""),
]), ]),
BoolOption("b2", ""), BoolOption("b2", ""),
BoolOption("d1", ""), BoolOption("d1", ""),
]) ])
c = Config(descr) c = Config(descr)
raises(PropertiesOptionError, "c.get('b1')") setting = c.cfgimpl_get_settings()
setting.read_write()
raises(NotFoundError, "c.get('b1')")

View file

@ -7,22 +7,20 @@ from tiramisu.config import *
from tiramisu.option import * from tiramisu.option import *
def make_description(): def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False, properties=(('hidden'),))
# hidding dummy here
gcdummy.hide()
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
wantref_option = BoolOption('wantref', 'Test requires', default=False, wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=[('gc.name', 'ref')]) requires=(('gc.name', 'ref', 'hidden'),))
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
default=False, default=False,
requires=[('gc.name', 'framework')]) requires=(('gc.name', 'framework', 'hidden'),))
# ____________________________________________________________ # ____________________________________________________________
booloptiontwo = BoolOption('booltwo', 'Test boolean option two', default=False) booloptiontwo = BoolOption('booltwo', 'Test boolean option two', default=False)
@ -38,20 +36,20 @@ def make_description():
#____________________________________________________________ #____________________________________________________________
#freeze #freeze
def make_description_freeze(): def make_description_freeze():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref') gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False) gcdummy = BoolOption('dummy', 'dummy', default=False)
objspaceoption = ChoiceOption('objspace', 'Object space', objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std') ('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True) booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0) intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3) floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc") stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True) boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Test requires', default=False, wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
wantframework_option = BoolOption('wantframework', 'Test requires', wantframework_option = BoolOption('wantframework', 'Test requires',
default=False, default=False,
requires=['boolop']) requires=(('boolop', True, 'hidden'),))
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption]) gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption, descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
@ -64,10 +62,10 @@ def test_freeze_whole_config():
descr = make_description_freeze() descr = make_description_freeze()
conf = Config(descr) conf = Config(descr)
settings = conf.cfgimpl_get_settings() settings = conf.cfgimpl_get_settings()
settings.freeze_everything() settings.enable_property('everything_frozen')
assert conf.gc.dummy == False assert conf.gc.dummy == False
raises(TypeError, "conf.gc.dummy = True") raises(TypeError, "conf.gc.dummy = True")
settings.un_freeze_everything() settings.disable_property('everything_frozen')
conf.gc.dummy = True conf.gc.dummy = True
assert conf.gc.dummy == True assert conf.gc.dummy == True
@ -75,8 +73,11 @@ def test_freeze_one_option():
"freeze an option " "freeze an option "
descr = make_description_freeze() descr = make_description_freeze()
conf = Config(descr) conf = Config(descr)
setting = conf.cfgimpl_get_settings()
setting.read_write()
#freeze only one option #freeze only one option
conf.gc._cfgimpl_descr.dummy.freeze() dummy = conf.unwrap_from_path('gc.dummy')
conf.gc.cfgimpl_get_settings().add_property('frozen', dummy)
assert conf.gc.dummy == False assert conf.gc.dummy == False
raises(TypeError, "conf.gc.dummy = True") raises(TypeError, "conf.gc.dummy = True")
@ -85,45 +86,48 @@ def test_frozen_value():
s = StrOption("string", "", default="string") s = StrOption("string", "", default="string")
descr = OptionDescription("options", "", [s]) descr = OptionDescription("options", "", [s])
config = Config(descr) config = Config(descr)
settings = config.cfgimpl_get_settings() settings = config.cfgimpl_get_settings().enable_property('frozen')
settings.freeze() config.cfgimpl_get_settings().add_property('frozen', s)
s.freeze()
raises(TypeError, 'config.string = "egg"') raises(TypeError, 'config.string = "egg"')
def test_freeze(): def test_freeze():
"freeze a whole configuration object" "freeze a whole configuration object"
descr = make_description() descr = make_description()
conf = Config(descr) conf = Config(descr)
settings = conf.cfgimpl_get_settings() settings = conf.cfgimpl_get_settings().enable_property('frozen')
settings.freeze()
name = conf.unwrap_from_path("gc.name") name = conf.unwrap_from_path("gc.name")
name.freeze() conf.cfgimpl_get_settings().add_property('frozen', name)
raises(TypeError, "conf.gc.name = 'framework'") raises(TypeError, "conf.gc.name = 'framework'")
# ____________________________________________________________ # ____________________________________________________________
def test_is_hidden(): def test_is_hidden():
descr = make_description() descr = make_description()
config = Config(descr) config = Config(descr)
assert config.gc._cfgimpl_descr.dummy._is_hidden() == True setting = config.cfgimpl_get_settings()
setting.read_write()
dummy = config.unwrap_from_path('gc.dummy')
assert not config.cfgimpl_get_settings().has_property('frozen', dummy)
# setattr # setattr
raises(PropertiesOptionError, "config.gc.dummy == False") raises(PropertiesOptionError, "config.gc.dummy == False")
# getattr # getattr
raises(PropertiesOptionError, "config.gc.dummy") raises(PropertiesOptionError, "config.gc.dummy")
# I want to access to this option anyway # I want to access to this option anyway
opt = config.unwrap_from_path("gc.dummy") opt = config.unwrap_from_path("gc.dummy")
assert config._cfgimpl_context._cfgimpl_values[opt] == False assert config.cfgimpl_get_values()[opt] == False
def test_group_is_hidden(): def test_group_is_hidden():
descr = make_description() descr = make_description()
config = Config(descr) config = Config(descr)
setting = config.cfgimpl_get_settings()
setting.read_write()
gc = config.unwrap_from_path('gc') gc = config.unwrap_from_path('gc')
dummy = config.unwrap_from_path('gc.dummy') dummy = config.unwrap_from_path('gc.dummy')
gc.hide() config.cfgimpl_get_settings().add_property('hidden', gc)
raises(PropertiesOptionError, "config.gc.dummy") raises(PropertiesOptionError, "config.gc.dummy")
assert gc._is_hidden() assert config.cfgimpl_get_settings().has_property('hidden', gc)
raises(PropertiesOptionError, "config.gc.float") raises(PropertiesOptionError, "config.gc.float")
# manually set the subconfigs to "show" # manually set the subconfigs to "show"
gc.show() config.cfgimpl_get_settings().del_property('hidden', gc)
assert gc._is_hidden() == False assert not config.cfgimpl_get_settings().has_property('hidden', gc)
assert config.gc.float == 2.3 assert config.gc.float == 2.3
#dummy est en hide #dummy est en hide
raises(PropertiesOptionError, "config.gc.dummy == False") raises(PropertiesOptionError, "config.gc.dummy == False")
@ -131,17 +135,22 @@ def test_group_is_hidden():
def test_global_show(): def test_global_show():
descr = make_description() descr = make_description()
config = Config(descr) config = Config(descr)
assert config.gc._cfgimpl_descr.dummy._is_hidden() == True setting = config.cfgimpl_get_settings()
setting.read_write()
dummy = config.unwrap_from_path('gc.dummy')
config.cfgimpl_get_settings().add_property('hidden', dummy)
assert config.cfgimpl_get_settings().has_property('hidden', dummy)
raises(PropertiesOptionError, "config.gc.dummy == False") raises(PropertiesOptionError, "config.gc.dummy == False")
def test_with_many_subgroups(): def test_with_many_subgroups():
descr = make_description() descr = make_description()
config = Config(descr) config = Config(descr)
assert config.gc.subgroup._cfgimpl_descr.booltwo._is_hidden() == False booltwo = config.unwrap_from_path('gc.subgroup.booltwo')
assert not config.cfgimpl_get_settings().has_property('hidden', booltwo)
assert config.gc.subgroup.booltwo == False assert config.gc.subgroup.booltwo == False
config.gc.subgroup._cfgimpl_descr.booltwo.hide() config.cfgimpl_get_settings().add_property('hidden', booltwo)
path = 'gc.subgroup.booltwo' path = 'gc.subgroup.booltwo'
homeconfig, name = config.cfgimpl_get_home_by_path(path) homeconfig, name = config.cfgimpl_get_home_by_path(path)
assert name == "booltwo" assert name == "booltwo"
option = getattr(homeconfig._cfgimpl_descr, name) option = getattr(homeconfig._cfgimpl_descr, name)
assert option._is_hidden() assert config.cfgimpl_get_settings().has_property('hidden', booltwo)

View file

@ -17,7 +17,7 @@ def make_description():
default=False) default=False)
adresse_serveur_ntp = StrOption('serveur_ntp', "adresse serveur ntp", multi=True) adresse_serveur_ntp = StrOption('serveur_ntp', "adresse serveur ntp", multi=True)
time_zone = ChoiceOption('time_zone', 'fuseau horaire du serveur', time_zone = ChoiceOption('time_zone', 'fuseau horaire du serveur',
['Paris', 'Londres'], 'Paris') ('Paris', 'Londres'), 'Paris')
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé") ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé")
netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau") netmask_admin_eth0 = StrOption('netmask_admin_eth0', "masque du sous-réseau")
@ -134,10 +134,10 @@ def test_values_with_master_and_slaves():
opt = cfg.unwrap_from_path("ip_admin_eth0.ip_admin_eth0") opt = cfg.unwrap_from_path("ip_admin_eth0.ip_admin_eth0")
owner = cfg._cfgimpl_context._cfgimpl_settings.getowner() owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
assert interface1.get_group_type() == groups.master assert interface1.get_group_type() == groups.master
assert opt.getowner(cfg) == owners.default assert cfg.cfgimpl_get_values().getowner(opt) == owners.default
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145") cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
assert cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"] assert cfg.ip_admin_eth0.ip_admin_eth0 == ["192.168.230.145"]
assert opt.getowner(cfg) == owner assert cfg.cfgimpl_get_values().getowner(opt) == owner
def test_reset_values_with_master_and_slaves(): def test_reset_values_with_master_and_slaves():
ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True) ip_admin_eth0 = StrOption('ip_admin_eth0', "ip réseau autorisé", multi=True)
@ -149,9 +149,9 @@ def test_reset_values_with_master_and_slaves():
opt = cfg.unwrap_from_path("ip_admin_eth0.ip_admin_eth0") opt = cfg.unwrap_from_path("ip_admin_eth0.ip_admin_eth0")
owner = cfg._cfgimpl_context._cfgimpl_settings.getowner() owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
assert interface1.get_group_type() == groups.master assert interface1.get_group_type() == groups.master
assert opt.getowner(cfg) == owners.default assert cfg.cfgimpl_get_values().getowner(opt) == owners.default
cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145") cfg.ip_admin_eth0.ip_admin_eth0.append("192.168.230.145")
assert opt.getowner(cfg) == owner assert cfg.cfgimpl_get_values().getowner(opt) == owner
cfg._cfgimpl_context._cfgimpl_values.reset(opt) cfg.cfgimpl_get_values().reset(opt)
assert opt.getowner(cfg) == owners.default assert cfg.cfgimpl_get_values().getowner(opt) == owners.default
assert cfg.ip_admin_eth0.ip_admin_eth0 == [] assert cfg.ip_admin_eth0.ip_admin_eth0 == []

View file

@ -1,4 +1,4 @@
# Copyright (C) 2012 Team tiramisu (see AUTHORS for all contributors) # Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -27,14 +27,15 @@ from tiramisu.error import PropertiesOptionError, ConflictConfigError
# # in case of an 'auto' and a 'fill' without a value, # # in case of an 'auto' and a 'fill' without a value,
# # we have to carry out a calculation # # we have to carry out a calculation
# return calc_factory(name, callback, callback_params, config) # return calc_factory(name, callback, callback_params, config)
def carry_out_calculation(name, option, config):
def carry_out_calculation(name, config, callback, callback_params):
# FIXME we have to know the exact status of the config # FIXME we have to know the exact status of the config
# not to disrupt it # not to disrupt it
# config.freeze() # config.freeze()
callback=option.getcallback() #callback, callback_params = option.getcallback()
callback_params=option.getcallback_params() #if callback_params is None:
if callback_params is None: # callback_params = {}
callback_params = {}
tcparams = {} tcparams = {}
one_is_multi = False one_is_multi = False
len_multi = 0 len_multi = 0
@ -44,7 +45,7 @@ def carry_out_calculation(name, option, config):
if type(value) == tuple: if type(value) == tuple:
path, check_disabled = value path, check_disabled = value
try: try:
opt_value = config._getattr(path, permissive=True) opt_value = config._getattr(path, force_permissive=True)
opt = config.unwrap_from_path(path) opt = config.unwrap_from_path(path)
except PropertiesOptionError, err: except PropertiesOptionError, err:
if check_disabled: if check_disabled:
@ -52,12 +53,14 @@ def carry_out_calculation(name, option, config):
raise PropertiesOptionError(err, err.proptype) raise PropertiesOptionError(err, err.proptype)
is_multi = opt.is_multi() is_multi = opt.is_multi()
if is_multi: if is_multi:
if opt_value != None: if opt_value is not None:
len_value = len(opt_value) len_value = len(opt_value)
if len_multi != 0 and len_multi != len_value: if len_multi != 0 and len_multi != len_value:
raise ConflictConfigError('unable to carry out a calculation, ' raise ConflictConfigError('unable to carry out '
'option values with multi types must have same length for: ' 'a calculation, option '
+ name) 'values with multi types'
' must have same length '
'for: ' + name)
len_multi = len_value len_multi = len_value
one_is_multi = True one_is_multi = True
tcparams.setdefault(key, []).append((opt_value, is_multi)) tcparams.setdefault(key, []).append((opt_value, is_multi))
@ -72,7 +75,7 @@ def carry_out_calculation(name, option, config):
for key, couples in tcparams.items(): for key, couples in tcparams.items():
for couple in couples: for couple in couples:
value, ismulti = couple value, ismulti = couple
if ismulti and value != None: if ismulti and value is not None:
if key == '': if key == '':
params.append(value[incr]) params.append(value[incr])
else: else:
@ -104,6 +107,7 @@ def carry_out_calculation(name, option, config):
tcp[key] = couple[0] tcp[key] = couple[0]
return calculate(name, callback, params, tcp) return calculate(name, callback, params, tcp)
def calculate(name, callback, params, tcparams): def calculate(name, callback, params, tcparams):
try: try:
# XXX not only creole... # XXX not only creole...
@ -113,5 +117,5 @@ def calculate(name, callback, params, tcparams):
import traceback import traceback
traceback.print_exc() traceback.print_exc()
raise ConflictConfigError("callback: {0} return error {1} for " raise ConflictConfigError("callback: {0} return error {1} for "
"option: {2}".format(callback, str(err), name)) "option: {2}".format(callback, str(err),
name))

View file

@ -1,77 +0,0 @@
# -*- coding: utf-8 -*-
"base 'interface' types for option types"
# Copyright (C) 2012-2013 Team tiramisu (see AUTHORS for all contributors)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# The original `Config` design model is unproudly borrowed from
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________
class CommonType(object):
def has_properties(self):
return bool(len(self.properties))
def has_property(self, propname):
return propname in self.properties
def get_properties(self):
return self.properties
def add_property(self, propname):
if not propname in self.properties:
self.properties.append(propname)
def del_property(self, propname):
if self.has_property(propname):
self.properties.remove(propname)
#class HiddenBaseType(BaseType):
# def hide(self):
# self.add_property('hidden')
# def show(self):
# self.del_property('hidden')
# def _is_hidden(self):
# # dangerous method: how an Option() can determine its status by itself ?
# return self.has_property('hidden')
#class DisabledBaseType(BaseType):
# def disable(self):
# self.add_property('disabled')
# def enable(self):
# self.del_property('disabled')
# def _is_disabled(self):
# return self.has_property('disabled')
# commonly used properties. Option and OptionDescription will have these methods
common_properties = {'hidden': ('hide', 'show', '_is_hidden'),
'disabled': ('disable', 'enable', '_is_disabled')
}
basetype_methods = dict()
def build_properties(prop, add_prop, del_prop, is_prop):
def add_property(self):
self.add_property(prop)
def del_property(self):
self.del_property(prop)
def is_property(self):
return self.has_property(prop)
return {add_prop:add_property, del_prop:del_property,
is_prop:is_property}
for propname, meth_names in common_properties.items():
basetype_methods.update(build_properties(propname, meth_names[0], meth_names[1],
meth_names[2]))
BaseType = type('BaseType', (CommonType,), basetype_methods)

View file

@ -20,134 +20,50 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from copy import copy #from inspect import getmembers, ismethod
from inspect import getmembers, ismethod from tiramisu.error import (PropertiesOptionError, NotFoundError,
from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError, AmbigousOptionError, NoMatchingOptionFound, MandatoryError)
AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
MandatoryError, MethodCallError, NoValueReturned)
from tiramisu.option import (OptionDescription, Option, SymLinkOption, from tiramisu.option import (OptionDescription, Option, SymLinkOption,
apply_requires) apply_requires)
from tiramisu.setting import groups, owners, Setting from tiramisu.setting import groups, Setting
from tiramisu.value import Values from tiramisu.value import Values
# ____________________________________________________________
class Config(object):
"main configuration management entry"
__slots__ = ('_cfgimpl_descr', '_cfgimpl_subconfigs',
'_cfgimpl_parent', '_cfgimpl_warnings', '_cfgimpl_permissive',
'_cfgimpl_context', '_cfgimpl_settings', '_cfgimpl_values',
'_cfgimpl_slots', '_cfgimpl_build_all_paths')
def __init__(self, descr, parent=None, context=None, valid_opt_names=True): class SubConfig(object):
"sub configuration management entry"
__slots__ = ('_cfgimpl_descr', '_cfgimpl_subconfigs', '_cfgimpl_parent',
'_cfgimpl_context')
def __init__(self, descr, parent, context): # FIXME , slots):
""" Configuration option management master class """ Configuration option management master class
:param descr: describes the configuration schema :param descr: describes the configuration schema
:type descr: an instance of ``option.OptionDescription`` :type descr: an instance of ``option.OptionDescription``
:param parent: is None if the ``Config`` is root parent Config otherwise :param parent: parent's `Config`
:type parent: ``Config`` :type parent: `Config`
:param context: the current root config :param context: the current root config
:type context: `Config` :type context: `Config`
""" """
# main option description # main option description
self._cfgimpl_descr = descr self._cfgimpl_descr = descr
# sub option descriptions # sub option descriptions
self._cfgimpl_subconfigs = {} self._cfgimpl_subconfigs = None
self._cfgimpl_parent = parent self._cfgimpl_parent = parent
self._cfgimpl_permissive = []
if context is None:
self._cfgimpl_context = self
else:
self._cfgimpl_context = context self._cfgimpl_context = context
if parent is None: #self._cfgimpl_build(slots)
self._cfgimpl_settings = Setting()
self._cfgimpl_settings.valid_opt_names = valid_opt_names
self._cfgimpl_values = Values(self._cfgimpl_context)
#self._cfgimpl_all_paths = {}
else:
if context is None:
raise ConfigError("cannot find a value for this config")
valid_opt_names = context._cfgimpl_settings.valid_opt_names
"warnings are a great idea, let's make up a better use of it"
self._cfgimpl_warnings = []
if valid_opt_names:
# some api members shall not be used as option's names !
methods = getmembers(self, ismethod)
self._cfgimpl_slots = [key for key, value in methods
if not key.startswith("_")]
else:
self._cfgimpl_slots = []
self._cfgimpl_build()
if parent is None:
self._cfgimpl_build_all_paths()
def _cfgimpl_build_all_paths(self): def cfgimpl_get_context(self):
self._cfgimpl_descr.build_cache() return self._cfgimpl_context
def cfgimpl_get_settings(self): def cfgimpl_get_settings(self):
return self._cfgimpl_context._cfgimpl_settings return self._cfgimpl_context._cfgimpl_settings
def cfgimpl_set_settings(self, settings): def cfgimpl_get_values(self):
if not isinstance(settings, Setting): return self._cfgimpl_context._cfgimpl_values
raise ConfigError("setting not allowed")
self._cfgimpl_context._cfgimpl_settings = settings
def cfgimpl_get_description(self): def cfgimpl_get_description(self):
return self._cfgimpl_descr return self._cfgimpl_descr
def cfgimpl_get_value(self, path):
"""same as multiple getattrs
:param path: list or tuple of path
example : ``path = (sub_conf, opt)`` makes a getattr of sub_conf, then
a getattr of opt, and then returns the opt's value.
:returns: subconf or option's value
"""
subpaths = list(path)
subconf_or_opt = self
for subpath in subpaths:
subconf_or_opt = getattr(subconf_or_opt, subpath)
return subconf_or_opt
def _validate_duplicates(self, children):
"""duplicates Option names in the schema
:type children: list of `Option` or `OptionDescription`
"""
duplicates = []
for dup in children:
if dup._name not in duplicates:
duplicates.append(dup._name)
else:
raise ConflictConfigError('duplicate option name: '
'{0}'.format(dup._name))
def _cfgimpl_build(self):
"""
- builds the config object from the schema
- settles various default values for options
"""
self._validate_duplicates(self._cfgimpl_descr._children)
if self._cfgimpl_descr.group_type == groups.master:
mastername = self._cfgimpl_descr._name
masteropt = getattr(self._cfgimpl_descr, mastername)
self._cfgimpl_context._cfgimpl_values.masters[masteropt] = []
for child in self._cfgimpl_descr._children:
if isinstance(child, OptionDescription):
self._validate_duplicates(child._children)
self._cfgimpl_subconfigs[child] = Config(child, parent=self,
context=self._cfgimpl_context)
else:
if child._name in self._cfgimpl_slots:
raise NameError("invalid name for the option:"
" {0}".format(child._name))
if (self._cfgimpl_descr.group_type == groups.master and
child != masteropt):
self._cfgimpl_context._cfgimpl_values.slaves[child] = masteropt
self._cfgimpl_context._cfgimpl_values.masters[masteropt].append(child)
# ____________________________________________________________ # ____________________________________________________________
# attribute methods # attribute methods
def __setattr__(self, name, value): def __setattr__(self, name, value):
@ -156,27 +72,30 @@ class Config(object):
#self.__dict__[name] = value #self.__dict__[name] = value
object.__setattr__(self, name, value) object.__setattr__(self, name, value)
return return
self._setattr(name, value)
def _setattr(self, name, value, force_permissive=False):
if '.' in name: if '.' in name:
homeconfig, name = self.cfgimpl_get_home_by_path(name) homeconfig, name = self.cfgimpl_get_home_by_path(name)
return setattr(homeconfig, name, value) return homeconfig.__setattr__(name, value)
if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption: if type(getattr(self._cfgimpl_descr, name)) != SymLinkOption:
self._validate(name, getattr(self._cfgimpl_descr, name)) self._validate(name, getattr(self._cfgimpl_descr, name), force_permissive=force_permissive)
if name in self._cfgimpl_slots:
raise NameError("invalid name for the option:"
" {0}".format(name))
self.setoption(name, value) self.setoption(name, value)
def _validate(self, name, opt_or_descr, permissive=False): def _validate(self, name, opt_or_descr, force_permissive=False):
"validation for the setattr and the getattr" "validation for the setattr and the getattr"
apply_requires(opt_or_descr, self, permissive=permissive) apply_requires(opt_or_descr, self)
if not isinstance(opt_or_descr, Option) and \ if not isinstance(opt_or_descr, Option) and \
not isinstance(opt_or_descr, OptionDescription): not isinstance(opt_or_descr, OptionDescription):
raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr))) raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr)))
properties = set(copy(opt_or_descr.properties)) properties = set(self.cfgimpl_get_settings().get_properties(opt_or_descr))
properties = properties & set(self._cfgimpl_context._cfgimpl_settings.get_properties()) #remove this properties, those properties are validate in value/setting
if permissive: properties = properties - set(['mandatory', 'frozen'])
properties = properties - set(self._cfgimpl_context._cfgimpl_settings.get_permissive()) set_properties = set(self.cfgimpl_get_settings().get_properties())
properties = properties - set(self._cfgimpl_permissive) properties = properties & set_properties
if force_permissive is True or self.cfgimpl_get_settings().has_property('permissive'):
properties = properties - set(self.cfgimpl_get_settings().get_permissive())
properties = properties - set(self.cfgimpl_get_settings().get_permissive(self.cfgimpl_get_description()))
properties = list(properties) properties = list(properties)
if properties != []: if properties != []:
raise PropertiesOptionError("trying to access" raise PropertiesOptionError("trying to access"
@ -187,22 +106,10 @@ class Config(object):
def __getattr__(self, name): def __getattr__(self, name):
return self._getattr(name) return self._getattr(name)
# def fill_multi(self, opt, result, use_default_multi=False, default_multi=None): def _getattr(self, name, force_permissive=False, force_properties=None):
# """fills a multi option with default and calculated values
# """
# # FIXME C'EST ENCORE DU N'IMPORTE QUOI
# if not isinstance(result, list):
# _result = [result]
# else:
# _result = result
# return Multi(_result, self._cfgimpl_context, opt)
def _getattr(self, name, permissive=False):
""" """
attribute notation mechanism for accessing the value of an option attribute notation mechanism for accessing the value of an option
:param name: attribute name :param name: attribute name
:param permissive: permissive doesn't raise some property error
(see ``permissive``)
:return: option's value if name is an option name, OptionDescription :return: option's value if name is an option name, OptionDescription
otherwise otherwise
""" """
@ -210,38 +117,222 @@ class Config(object):
# for instance getattr(self, "creole.general.family.adresse_ip_eth0") # for instance getattr(self, "creole.general.family.adresse_ip_eth0")
if '.' in name: if '.' in name:
homeconfig, name = self.cfgimpl_get_home_by_path(name) homeconfig, name = self.cfgimpl_get_home_by_path(name)
return homeconfig._getattr(name, permissive) return homeconfig._getattr(name)
opt_or_descr = getattr(self._cfgimpl_descr, name) opt_or_descr = getattr(self._cfgimpl_descr, name)
# symlink options # symlink options
if type(opt_or_descr) == SymLinkOption: if type(opt_or_descr) == SymLinkOption:
rootconfig = self._cfgimpl_get_toplevel() rootconfig = self.cfgimpl_get_context()
return getattr(rootconfig, opt_or_descr.path) path = rootconfig.cfgimpl_get_description().get_path_by_opt(opt_or_descr.opt)
return getattr(rootconfig, path)
self._validate(name, opt_or_descr, permissive) self._validate(name, opt_or_descr, force_permissive=force_permissive)
if isinstance(opt_or_descr, OptionDescription): if isinstance(opt_or_descr, OptionDescription):
if opt_or_descr not in self._cfgimpl_subconfigs: children = self.cfgimpl_get_description()._children
raise AttributeError("%s with name %s object has no attribute %s" % if opt_or_descr not in children[1]:
(self.__class__, opt_or_descr._name, name)) raise AttributeError("{0} with name {1} object has "
return self._cfgimpl_subconfigs[opt_or_descr] "no attribute {2}".format(self.__class__,
opt_or_descr._name,
name))
return SubConfig(opt_or_descr, self, self._cfgimpl_context)
# special attributes # special attributes
if name.startswith('_cfgimpl_'): if name.startswith('_cfgimpl_'):
# if it were in __dict__ it would have been found already # if it were in __dict__ it would have been found already
return self.__dict__[name] object.__getattr__(self, name)
return self._cfgimpl_context._cfgimpl_values[opt_or_descr] return self.cfgimpl_get_values()._getitem(opt_or_descr,
force_properties=force_properties)
def unwrap_from_name(self, name): def setoption(self, name, value, who=None):
"""convenience method to extract and Option() object from the Config() """effectively modifies the value of an Option()
**and it is slow**: it recursively searches into the namespaces (typically called by the __setattr__)
:returns: Option()
""" """
paths = self.getpaths(allpaths=True) child = getattr(self._cfgimpl_descr, name)
opts = dict([(path, self.unwrap_from_path(path)) for path in paths]) child.setoption(self, value)
all_paths = [p.split(".") for p in self.getpaths()]
for pth in all_paths: def cfgimpl_get_home_by_path(self, path):
if name in pth: """:returns: tuple (config, name)"""
return opts[".".join(pth)] path = path.split('.')
raise NotFoundError("name: {0} not found".format(name)) for step in path[:-1]:
self = getattr(self, step)
return self, path[-1]
def _cfgimpl_get_path(self):
"the path in the attribute access meaning."
#FIXME optimisation
subpath = []
obj = self
while obj._cfgimpl_parent is not None:
subpath.insert(0, obj._cfgimpl_descr._name)
obj = obj._cfgimpl_parent
return ".".join(subpath)
def getkey(self):
return self._cfgimpl_descr.getkey(self)
def __hash__(self):
return hash(self.getkey())
def __eq__(self, other):
"Config comparison"
if not isinstance(other, Config):
return False
return self.getkey() == other.getkey()
def __ne__(self, other):
"Config comparison"
return not self == other
# ______________________________________________________________________
def __iter__(self):
"""Pythonesque way of parsing group's ordered options.
iteration only on Options (not OptionDescriptions)"""
for child in self._cfgimpl_descr._children[1]:
if not isinstance(child, OptionDescription):
try:
yield child._name, getattr(self, child._name)
except GeneratorExit:
raise StopIteration
except:
pass # option with properties
def iter_all(self):
"""A way of parsing options **and** groups.
iteration on Options and OptionDescriptions."""
for child in self._cfgimpl_descr._children[1]:
try:
yield child._name, getattr(self, child._name)
except GeneratorExit:
raise StopIteration
except:
pass # option with properties
def iter_groups(self, group_type=None):
"""iteration on groups objects only.
All groups are returned if `group_type` is `None`, otherwise the groups
can be filtered by categories (families, or whatever).
:param group_type: if defined, is an instance of `groups.GroupType`
or `groups.MasterGroupType` that lives in
`setting.groups`
"""
if group_type is not None:
if not isinstance(group_type, groups.GroupType):
raise TypeError("Unknown group_type: {0}".format(group_type))
for child in self._cfgimpl_descr._children[1]:
if isinstance(child, OptionDescription):
try:
if group_type is not None:
if child.get_group_type() == group_type:
yield child._name, getattr(self, child._name)
else:
yield child._name, getattr(self, child._name)
except GeneratorExit:
raise StopIteration
except:
pass
# ______________________________________________________________________
def cfgimpl_set_permissive(self, permissive):
if not isinstance(permissive, list):
raise TypeError('permissive must be a list')
self.cfgimpl_get_settings().set_permissive(permissive, self.cfgimpl_get_description())
# ______________________________________________________________________
def __str__(self):
"Config's string representation"
lines = []
for name, grp in self.iter_groups():
lines.append("[%s]" % name)
for name, value in self:
try:
lines.append("%s = %s" % (name, value))
except:
pass
return '\n'.join(lines)
__repr__ = __str__
def getpaths(self, include_groups=False, allpaths=False, mandatory=False):
"""returns a list of all paths in self, recursively, taking care of
the context of properties (hidden/disabled)
:param include_groups: if true, OptionDescription are included
:param allpaths: all the options (event the properties protected ones)
:param mandatory: includes the mandatory options
:returns: list of all paths
"""
paths = []
for path in self._cfgimpl_descr.getpaths(include_groups=include_groups):
if allpaths:
paths.append(path)
else:
try:
getattr(self, path)
except MandatoryError:
if mandatory:
paths.append(path)
except PropertiesOptionError:
pass
else:
paths.append(path)
return paths
def getpath(self):
descr = self.cfgimpl_get_description()
context_descr = self.cfgimpl_get_context().cfgimpl_get_description()
return context_descr.get_path_by_opt(descr)
def get(self, name):
path = self.getpath()
return self.cfgimpl_get_context().get(name, _subpath=path)
def find(self, bytype=None, byname=None, byvalue=None, byattrs=None):
path = self.getpath()
return self.cfgimpl_get_context().find(bytype=bytype, byname=byname,
byvalue=byvalue,
byattrs=byattrs,
_subpath=path)
def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None):
path = self.getpath()
return self.cfgimpl_get_context().find_first(bytype=bytype,
byname=byname,
byvalue=byvalue,
byattrs=byattrs,
_subpath=path)
# ____________________________________________________________
class Config(SubConfig):
"main configuration management entry"
__slots__ = ('_cfgimpl_settings', '_cfgimpl_values')
def __init__(self, descr, valid_opt_names=True):
""" Configuration option management master class
:param descr: describes the configuration schema
:type descr: an instance of ``option.OptionDescription``
:param parent: is None if the ``Config`` is root parent Config otherwise
:type parent: ``Config``
:param context: the current root config
:type context: `Config`
"""
self._cfgimpl_settings = Setting()
self._cfgimpl_values = Values(self)
#if valid_opt_names:
# # some api members shall not be used as option's names !
# #FIXME fait une boucle infini ...
# #methods = getmembers(self, ismethod)
# #slots = tuple([key for key, value in methods
# # if not key.startswith("_")])
# slots = []
#else:
# slots = []
super(Config, self).__init__(descr, None, self) # , slots)
self._cfgimpl_build_all_paths()
def _cfgimpl_build_all_paths(self):
self._cfgimpl_descr.build_cache()
def unwrap_from_path(self, path): def unwrap_from_path(self, path):
"""convenience method to extract and Option() object from the Config() """convenience method to extract and Option() object from the Config()
@ -255,13 +346,6 @@ class Config(object):
return getattr(homeconfig._cfgimpl_descr, path) return getattr(homeconfig._cfgimpl_descr, path)
return getattr(self._cfgimpl_descr, path) return getattr(self._cfgimpl_descr, path)
def setoption(self, name, value, who=None):
"""effectively modifies the value of an Option()
(typically called by the __setattr__)
"""
child = getattr(self._cfgimpl_descr, name)
child.setoption(self, value)
def set(self, **kwargs): def set(self, **kwargs):
""" """
do what I mean"-interface to option setting. Searches all paths do what I mean"-interface to option setting. Searches all paths
@ -292,7 +376,7 @@ class Config(object):
'there is no option that matches %s' 'there is no option that matches %s'
' or the option is hidden or disabled' % (key, )) ' or the option is hidden or disabled' % (key, ))
def get(self, name): def get(self, name, _subpath=None):
""" """
same as a `find_first()` method in a config that has identical names: same as a `find_first()` method in a config that has identical names:
it returns the first item of an option named `name` it returns the first item of an option named `name`
@ -300,181 +384,46 @@ class Config(object):
much like the attribute access way, except that much like the attribute access way, except that
the search for the option is performed recursively in the whole the search for the option is performed recursively in the whole
configuration tree. configuration tree.
**carefull**: very slow !
:returns: option value. :returns: option value.
""" """
paths = self.getpaths(allpaths=True) return self._find(byname=name, bytype=None, byvalue=None, byattrs=None,
pathsvalues = [] first=True, getvalue=True, _subpath=_subpath)
for path in paths:
pathname = path.split('.')[-1]
if pathname == name:
try:
value = getattr(self, path)
return value
except Exception, e:
raise e
raise NotFoundError("option {0} not found in config".format(name))
def cfgimpl_get_home_by_path(self, path): def _find(self, bytype, byname, byvalue, byattrs, first, getvalue=False,
""":returns: tuple (config, name)""" _subpath=None):
path = path.split('.')
for step in path[:-1]:
self = getattr(self, step)
return self, path[-1]
def _cfgimpl_get_toplevel(self):
":returns: root config"
while self._cfgimpl_parent is not None:
self = self._cfgimpl_parent
return self
def _cfgimpl_get_path(self):
"the path in the attribute access meaning."
subpath = []
obj = self
while obj._cfgimpl_parent is not None:
subpath.insert(0, obj._cfgimpl_descr._name)
obj = obj._cfgimpl_parent
return ".".join(subpath)
# ______________________________________________________________________
# def cfgimpl_previous_value(self, path):
# "stores the previous value"
# home, name = self.cfgimpl_get_home_by_path(path)
# # FIXME fucking name
# return home._cfgimpl_context._cfgimpl_values.previous_values[name]
# def get_previous_value(self, name):
# "for the time being, only the previous Option's value is accessible"
# return self._cfgimpl_context._cfgimpl_values.previous_values[name]
# ______________________________________________________________________
def add_warning(self, warning):
"Config implements its own warning pile. Could be useful"
self._cfgimpl_get_toplevel()._cfgimpl_warnings.append(warning)
def get_warnings(self):
"Config implements its own warning pile"
return self._cfgimpl_get_toplevel()._cfgimpl_warnings
# ____________________________________________________________
def getkey(self):
return self._cfgimpl_descr.getkey(self)
def __hash__(self):
return hash(self.getkey())
def __eq__(self, other):
"Config comparison"
if not isinstance(other, Config):
return False
return self.getkey() == other.getkey()
def __ne__(self, other):
"Config comparison"
return not self == other
# ______________________________________________________________________
def __iter__(self):
"""Pythonesque way of parsing group's ordered options.
iteration only on Options (not OptionDescriptions)"""
for child in self._cfgimpl_descr._children:
if not isinstance(child, OptionDescription):
try:
yield child._name, getattr(self, child._name)
except GeneratorExit:
raise StopIteration
except:
pass # option with properties
def iter_all(self):
"""A way of parsing options **and** groups.
iteration on Options and OptionDescriptions."""
for child in self._cfgimpl_descr._children:
try:
yield child._name, getattr(self, child._name)
except GeneratorExit:
raise StopIteration
except:
pass # option with properties
def iter_groups(self, group_type=None):
"""iteration on groups objects only.
All groups are returned if `group_type` is `None`, otherwise the groups
can be filtered by categories (families, or whatever).
:param group_type: if defined, is an instance of `groups.GroupType`
or `groups.MasterGroupType` that lives in
`setting.groups`
"""
if group_type is not None:
if not isinstance(group_type, groups.GroupType):
raise TypeError("Unknown group_type: {0}".format(group_type))
for child in self._cfgimpl_descr._children:
if isinstance(child, OptionDescription):
try:
if group_type is not None:
if child.get_group_type() == group_type:
yield child._name, getattr(self, child._name)
else:
yield child._name, getattr(self, child._name)
except GeneratorExit:
raise StopIteration
except:
pass
# ______________________________________________________________________
def cfgimpl_set_permissive(self, permissive):
if not isinstance(permissive, list):
raise TypeError('permissive must be a list')
self._cfgimpl_permissive = permissive
# ______________________________________________________________________
def __str__(self):
"Config's string representation"
lines = []
for name, grp in self.iter_groups():
lines.append("[%s]" % name)
for name, value in self:
try:
lines.append("%s = %s" % (name, value))
except:
pass
return '\n'.join(lines)
__repr__ = __str__
def getpaths(self, include_groups=False, allpaths=False, mandatory=False):
"""returns a list of all paths in self, recursively, taking care of
the context of properties (hidden/disabled)
:param include_groups: if true, OptionDescription are included
:param allpaths: all the options (event the properties protected ones)
:param mandatory: includes the mandatory options
:returns: list of all paths
"""
paths = []
for path in self._cfgimpl_descr.getpaths(include_groups=include_groups):
if allpaths:
paths.append(path)
else:
try:
value = getattr(self, path)
except MandatoryError:
if mandatory:
paths.append(path)
except PropertiesOptionError:
pass
else:
paths.append(path)
return paths
def _find(self, bytype, byname, byvalue, byattrs, first):
""" """
convenience method for finding an option that lives only in the subtree convenience method for finding an option that lives only in the subtree
:param first: return only one option if True, a list otherwise :param first: return only one option if True, a list otherwise
:return: find list or an exception if nothing has been found :return: find list or an exception if nothing has been found
""" """
def _filter_by_name():
if byname is None:
return True
if path == byname or path.endswith('.' + byname):
return True
else:
return False
def _filter_by_value():
if byvalue is None:
return True
try:
value = getattr(self, path)
if value == byvalue:
return True
except: # a property restricts the access of the value
pass
return False
def _filter_by_type():
if bytype is None:
return True
if isinstance(option, bytype):
return True
return False
def _filter_by_attrs(): def _filter_by_attrs():
if byattrs is None: if byattrs is None:
return True return True
@ -487,37 +436,15 @@ class Config(object):
else: else:
continue continue
return True return True
def _filter_by_name():
if byname is None:
return True
pathname = path.split('.')[-1]
if pathname == byname:
return True
else:
return False
def _filter_by_value():
if byvalue is None:
return True
try:
value = getattr(self, path)
if value == byvalue:
return True
except: # a property restricts the access of the value
pass
return False
def _filter_by_type():
if bytype is None:
return True
if isinstance(option, bytype):
return True
return False
find_results = [] find_results = []
paths = self.getpaths(allpaths=True) opts, paths = self.cfgimpl_get_description()._cache_paths
for path in paths: for index in range(0, len(paths)):
try: path = paths[index]
option = self.unwrap_from_path(path) option = opts[index]
except PropertiesOptionError, err: if isinstance(option, OptionDescription):
continue
if _subpath is not None and not path.startswith(_subpath + '.'):
continue continue
if not _filter_by_name(): if not _filter_by_name():
continue continue
@ -527,17 +454,27 @@ class Config(object):
continue continue
if not _filter_by_attrs(): if not _filter_by_attrs():
continue continue
#remove option with propertyerror, ...
try:
value = getattr(self, path)
except: # a property restricts the access of the value
continue
if first: if first:
if getvalue:
return value
else:
return option return option
else:
if getvalue:
find_results.append(value)
else: else:
find_results.append(option) find_results.append(option)
if find_results == []: if find_results == []:
raise NotFoundError("no option found in config with these criteria") raise NotFoundError("no option found in config with these criteria")
else: else:
return find_results return find_results
def find(self, bytype=None, byname=None, byvalue=None, byattrs=None): def find(self, bytype=None, byname=None, byvalue=None, byattrs=None, _subpath=None):
""" """
finds a list of options recursively in the config finds a list of options recursively in the config
@ -547,9 +484,9 @@ class Config(object):
:param byattrs: dict of option attributes (default, callback...) :param byattrs: dict of option attributes (default, callback...)
:returns: list of matching Option objects :returns: list of matching Option objects
""" """
return self._find(bytype, byname, byvalue, byattrs, first=False) return self._find(bytype, byname, byvalue, byattrs, first=False, _subpath=_subpath)
def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None): def find_first(self, bytype=None, byname=None, byvalue=None, byattrs=None, _subpath=None):
""" """
finds an option recursively in the config finds an option recursively in the config
@ -559,7 +496,7 @@ class Config(object):
:param byattrs: dict of option attributes (default, callback...) :param byattrs: dict of option attributes (default, callback...)
:returns: list of matching Option objects :returns: list of matching Option objects
""" """
return self._find(bytype, byname, byvalue, byattrs, first=True) return self._find(bytype, byname, byvalue, byattrs, first=True, _subpath=_subpath)
def make_dict(config, flatten=False): def make_dict(config, flatten=False):
@ -586,15 +523,11 @@ def mandatory_warnings(config):
where no value has been set where no value has been set
:returns: generator of mandatory Option's path :returns: generator of mandatory Option's path
FIXME : CAREFULL : not multi-user
""" """
mandatory = config._cfgimpl_context._cfgimpl_settings.mandatory for path in config.cfgimpl_get_description().getpaths(include_groups=True):
config._cfgimpl_context._cfgimpl_settings.mandatory = True
for path in config._cfgimpl_descr.getpaths(include_groups=True):
try: try:
value = config._getattr(path, permissive=True) config._getattr(path, force_properties=('mandatory',))
except MandatoryError: except MandatoryError:
yield path yield path
except PropertiesOptionError: except PropertiesOptionError:
pass pass
config._cfgimpl_context._cfgimpl_settings.mandatory = mandatory

View file

@ -21,26 +21,17 @@
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
import re import re
from copy import copy
from types import FunctionType from types import FunctionType
from tiramisu.basetype import BaseType from tiramisu.error import (ConfigError, NotFoundError, ConflictConfigError,
from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError, RequiresError, RequirementRecursionError,
RequiresError, RequirementRecursionError, MandatoryError,
PropertiesOptionError) PropertiesOptionError)
from tiramisu.autolib import carry_out_calculation from tiramisu.autolib import carry_out_calculation
from tiramisu.setting import groups, owners from tiramisu.setting import groups, multitypes
requires_actions = [('hide', 'show'), ('enable', 'disable'),
('freeze', 'unfreeze')]
available_actions = []
reverse_actions = {}
for act1, act2 in requires_actions:
available_actions.extend([act1, act2])
reverse_actions[act1] = act2
reverse_actions[act2] = act1
# ____________________________________________________________
name_regexp = re.compile(r'^\d+') name_regexp = re.compile(r'^\d+')
def valid_name(name): def valid_name(name):
try: try:
name = str(name) name = str(name)
@ -53,7 +44,9 @@ def valid_name(name):
#____________________________________________________________ #____________________________________________________________
# #
class BaseInformation:
class BaseInformation(object):
__slots__ = ('informations')
def set_information(self, key, value): def set_information(self, key, value):
"""updates the information's attribute """updates the information's attribute
@ -64,29 +57,33 @@ class BaseInformation:
""" """
self.informations[key] = value self.informations[key] = value
def get_information(self, key): def get_information(self, key, default=None):
"""retrieves one information's item """retrieves one information's item
:param key: the item string (ex: "help") :param key: the item string (ex: "help")
""" """
if key in self.informations: if key in self.informations:
return self.informations[key] return self.informations[key]
elif default is not None:
return default
else: else:
raise ValueError("Information's item not found: {0}".format(key)) raise ValueError("Information's item not found: {0}".format(key))
class Option(BaseType, BaseInformation):
class Option(BaseInformation):
""" """
Abstract base class for configuration option's. Abstract base class for configuration option's.
Reminder: an Option object is **not** a container for the value Reminder: an Option object is **not** a container for the value
""" """
#freeze means: cannot modify the value of an Option once set __slots__ = ('_name', '_requires', 'multi', '_validator', 'default_multi',
_frozen = False 'default', '_properties', 'callback', 'multitype',
#if an Option has been frozen, shall return the default value 'master_slaves')
_force_default_on_freeze = False
def __init__(self, name, doc, default=None, default_multi=None, def __init__(self, name, doc, default=None, default_multi=None,
requires=None, mandatory=False, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args={}): callback_params=None, validator=None, validator_args=None,
properties=None):
""" """
:param name: the option's name :param name: the option's name
:param doc: the option's description :param doc: the option's description
@ -107,51 +104,58 @@ class Option(BaseType, BaseInformation):
if not valid_name(name): if not valid_name(name):
raise NameError("invalid name: {0} for option".format(name)) raise NameError("invalid name: {0} for option".format(name))
self._name = name self._name = name
self.doc = doc self.informations = {}
self.set_information('doc', doc)
validate_requires_arg(requires, self._name)
self._requires = requires self._requires = requires
self._mandatory = mandatory
self.multi = multi self.multi = multi
self._validator = None #self._validator_args = None
self._validator_args = None
if validator is not None: if validator is not None:
if type(validator) != FunctionType: if type(validator) != FunctionType:
raise TypeError("validator must be a function") raise TypeError("validator must be a function")
self._validator = validator self._validator = (validator, validator_args)
if validator_args is not None: else:
self._validator_args = validator_args self._validator = None
if not self.multi and default_multi is not None: if not self.multi and default_multi is not None:
raise ConfigError("a default_multi is set whereas multi is False" raise ConfigError("a default_multi is set whereas multi is False"
" in option: {0}".format(name)) " in option: {0}".format(name))
if default_multi is not None and not self._validate(default_multi): if default_multi is not None and not self._validate(default_multi):
raise ConfigError("invalid default_multi value {0} " raise ConfigError("invalid default_multi value {0} "
"for option {1}".format(str(default_multi), name)) "for option {1}".format(str(default_multi), name))
self.default_multi = default_multi
#if self.multi and default_multi is None:
# _cfgimpl_warnings[name] = DefaultMultiWarning
if callback is not None and (default is not None or default_multi is not None): if callback is not None and (default is not None or default_multi is not None):
raise ConfigError("defaut values not allowed if option: {0} " raise ConfigError("defaut values not allowed if option: {0} "
"is calculated".format(name)) "is calculated".format(name))
self.callback = callback if callback is None and callback_params is not None:
if self.callback is None and callback_params is not None: raise ConfigError("params defined for a callback function but "
raise ConfigError("params defined for a callback function but" "no callback defined yet for option {0}".format(name))
" no callback defined yet for option {0}".format(name)) if callback is not None:
self.callback_params = callback_params self.callback = (callback, callback_params)
if self.multi == True: else:
if default == None: self.callback = None
if self.multi:
if default is None:
default = [] default = []
if not isinstance(default, list): if not isinstance(default, list):
raise ConfigError("invalid default value {0} " raise ConfigError("invalid default value {0} "
"for option {1} : not list type".format(str(default), name)) "for option {1} : not list type"
"".format(str(default), name))
if not self.validate(default, False): if not self.validate(default, False):
raise ConfigError("invalid default value {0} " raise ConfigError("invalid default value {0} "
"for option {1}".format(str(default), name)) "for option {1}"
"".format(str(default), name))
self.multitype = multitypes.default
self.default_multi = default_multi
else: else:
if default != None and not self.validate(default, False): if default is not None and not self.validate(default, False):
raise ConfigError("invalid default value {0} " raise ConfigError("invalid default value {0} "
"for option {1}".format(str(default), name)) "for option {1}".format(str(default), name))
self.default = default self.default = default
self.properties = [] # 'hidden', 'disabled'... if properties is None:
self.informations = {} properties = ()
if not isinstance(properties, tuple):
raise ConfigError('invalid properties type {0} for {1},'
' must be a tuple'.format(type(properties), self._name))
self._properties = properties # 'hidden', 'disabled'...
def validate(self, value, validate=True): def validate(self, value, validate=True):
""" """
@ -159,25 +163,25 @@ class Option(BaseType, BaseInformation):
:param validate: if true enables ``self._validator`` validation :param validate: if true enables ``self._validator`` validation
""" """
# generic calculation # generic calculation
if self.multi == False: if not self.multi:
# None allows the reset of the value # None allows the reset of the value
if value != None: if value is not None:
# customizing the validator # customizing the validator
if validate and self._validator is not None and \ if validate and self._validator is not None and \
not self._validator(value, **self._validator_args): not self._validator[0](value, **self._validator[1]):
return False return False
return self._validate(value) return self._validate(value)
else: else:
if not isinstance(value, list): if not isinstance(value, list):
raise ConfigError("invalid value {0} " raise ConfigError("invalid value {0} "
"for option {1} which must be a list".format(value, "for option {1} which must be a list"
self._name)) "".format(value, self._name))
for val in value: for val in value:
# None allows the reset of the value # None allows the reset of the value
if val != None: if val is not None:
# customizing the validator # customizing the validator
if validate and self._validator is not None and \ if validate and self._validator is not None and \
not self._validator(val, **self._validator_args): not self._validator[0](val, **self._validator[1]):
return False return False
if not self._validate(val): if not self._validate(val):
return False return False
@ -185,7 +189,7 @@ class Option(BaseType, BaseInformation):
def getdefault(self, default_multi=False): def getdefault(self, default_multi=False):
"accessing the default value" "accessing the default value"
if default_multi == False or not self.is_multi(): if not default_multi or not self.is_multi():
return self.default return self.default
else: else:
return self.getdefault_multi() return self.getdefault_multi()
@ -196,148 +200,88 @@ class Option(BaseType, BaseInformation):
def is_empty_by_default(self): def is_empty_by_default(self):
"no default value has been set yet" "no default value has been set yet"
if ((not self.is_multi() and self.default == None) or if ((not self.is_multi() and self.default is None) or
(self.is_multi() and (self.default == [] or None in self.default))): (self.is_multi() and (self.default == [] or None in self.default))):
return True return True
return False return False
def force_default(self):
"if an Option has been frozen, shall return the default value"
self._force_default_on_freeze = True
def hascallback_and_isfrozen():
return self._frozen and self.has_callback()
def is_forced_on_freeze(self):
"if an Option has been frozen, shall return the default value"
return self._frozen and self._force_default_on_freeze
def getdoc(self): def getdoc(self):
"accesses the Option's doc" "accesses the Option's doc"
return self.doc return self.get_information('doc')
def getcallback(self):
"a callback is only a link, the name of an external hook"
return self.callback
def has_callback(self): def has_callback(self):
"to know if a callback has been defined or not" "to know if a callback has been defined or not"
if self.callback == None: if self.callback is None:
return False return False
else: else:
return True return True
def getcallback_value(self, config): def getcallback_value(self, config):
return carry_out_calculation(self._name, callback, callback_params = self.callback
option=self, config=config) if callback_params is None:
callback_params = {}
def getcallback_params(self): return carry_out_calculation(self._name, config=config,
"if a callback has been defined, returns his arity" callback=callback,
return self.callback_params callback_params=callback_params)
def setowner(self, config, owner):
"""
:param config: *must* be only the **parent** config
(not the toplevel config)
:param owner: is a **real** owner, that is an object
that lives in setting.owners
"""
name = self._name
if not isinstance(owner, owners.Owner):
raise ConfigError("invalid type owner for option: {0}".format(
str(name)))
config._cfgimpl_context._cfgimpl_values.owners[self] = owner
def getowner(self, config):
"config *must* be only the **parent** config (not the toplevel config)"
return config._cfgimpl_context._cfgimpl_values.getowner(self)
def get_previous_value(self, config):
return config._cfgimpl_context._cfgimpl_values.get_previous_value(self)
def reset(self, config): def reset(self, config):
"""resets the default value and owner """resets the default value and owner
""" """
config._cfgimpl_context._cfgimpl_values.reset(self) config._cfgimpl_context._cfgimpl_values.reset(self)
def is_default_owner(self, config):
"""
:param config: *must* be only the **parent** config
(not the toplevel config)
:return: boolean
"""
return self.getowner(config) == owners.default
def setoption(self, config, value): def setoption(self, config, value):
"""changes the option's value with the value_owner's who """changes the option's value with the value_owner's who
:param config: the parent config is necessary here to store the value :param config: the parent config is necessary here to store the value
""" """
name = self._name name = self._name
rootconfig = config._cfgimpl_get_toplevel() setting = config.cfgimpl_get_settings()
if not self.validate(value, if not self.validate(value, setting.has_property('validator')):
config._cfgimpl_context._cfgimpl_settings.validator):
raise ConfigError('invalid value %s for option %s' % (value, name)) raise ConfigError('invalid value %s for option %s' % (value, name))
if self.is_mandatory(): if self not in config._cfgimpl_descr._children[1]:
# value shall not be '' for a mandatory option
# so '' is considered as being None
if not self.is_multi() and value == '':
value = None
# if self.is_multi() and '' in value:
# value = Multi([{'': None}.get(i, i) for i in value],
# config._cfgimpl_context, self)
if config._cfgimpl_context._cfgimpl_settings.is_mandatory() \
and ((self.is_multi() and value == []) or \
(not self.is_multi() and value is None)):
raise MandatoryError('cannot change the value to %s for '
'option %s' % (value, name))
if self not in config._cfgimpl_descr._children:
raise AttributeError('unknown option %s' % (name)) raise AttributeError('unknown option %s' % (name))
if config._cfgimpl_context._cfgimpl_settings.is_frozen_for_everything(): if setting.has_property('everything_frozen'):
raise TypeError("cannot set a value to the option {} if the whole " raise TypeError("cannot set a value to the option {} if the whole "
"config has been frozen".format(name)) "config has been frozen".format(name))
if config._cfgimpl_context._cfgimpl_settings.is_frozen() \ if setting.has_property('frozen') and setting.has_property('frozen',
and self.is_frozen(): self):
raise TypeError('cannot change the value to %s for ' raise TypeError('cannot change the value to %s for '
'option %s this option is frozen' % (str(value), name)) 'option %s this option is frozen' % (str(value), name))
apply_requires(self, config) apply_requires(self, config)
config._cfgimpl_context._cfgimpl_values[self] = value config.cfgimpl_get_values()[self] = value
def getkey(self, value): def getkey(self, value):
return value return value
# ____________________________________________________________
"freeze utility"
def freeze(self):
self._frozen = True
return True
def unfreeze(self):
self._frozen = False
def is_frozen(self):
return self._frozen
# ____________________________________________________________
def is_multi(self): def is_multi(self):
return self.multi return self.multi
def is_mandatory(self):
return self._mandatory
class ChoiceOption(Option): class ChoiceOption(Option):
__slots__ = ('values', 'open_values', 'opt_type')
opt_type = 'string' opt_type = 'string'
def __init__(self, name, doc, values, default=None, default_multi=None, def __init__(self, name, doc, values, default=None, default_multi=None,
requires=None, mandatory=False, multi=False, callback=None, requires=None, multi=False, callback=None,
callback_params=None, open_values=False, validator=None, callback_params=None, open_values=False, validator=None,
validator_args={}): validator_args=None, properties=()):
if not isinstance(values, tuple):
raise ConfigError('values must be a tuple for {0}'.format(name))
self.values = values self.values = values
if open_values not in [True, False]: if open_values not in (True, False):
raise ConfigError('Open_values must be a boolean for ' raise ConfigError('Open_values must be a boolean for '
'{0}'.format(name)) '{0}'.format(name))
self.open_values = open_values self.open_values = open_values
super(ChoiceOption, self).__init__(name, doc, default=default, super(ChoiceOption, self).__init__(name, doc, default=default,
default_multi=default_multi, callback=callback, default_multi=default_multi,
callback_params=callback_params, requires=requires, callback=callback,
multi=multi, mandatory=mandatory, validator=validator, callback_params=callback_params,
validator_args=validator_args) requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
properties=properties)
def _validate(self, value): def _validate(self, value):
if not self.open_values: if not self.open_values:
@ -345,72 +289,91 @@ class ChoiceOption(Option):
else: else:
return True return True
class BoolOption(Option): class BoolOption(Option):
__slots__ = ('opt_type')
opt_type = 'bool' opt_type = 'bool'
def _validate(self, value): def _validate(self, value):
return isinstance(value, bool) return isinstance(value, bool)
class IntOption(Option): class IntOption(Option):
__slots__ = ('opt_type')
opt_type = 'int' opt_type = 'int'
def _validate(self, value): def _validate(self, value):
return isinstance(value, int) return isinstance(value, int)
class FloatOption(Option): class FloatOption(Option):
__slots__ = ('opt_type')
opt_type = 'float' opt_type = 'float'
def _validate(self, value): def _validate(self, value):
return isinstance(value, float) return isinstance(value, float)
class StrOption(Option): class StrOption(Option):
__slots__ = ('opt_type')
opt_type = 'string' opt_type = 'string'
def _validate(self, value): def _validate(self, value):
return isinstance(value, str) return isinstance(value, str)
class UnicodeOption(Option): class UnicodeOption(Option):
__slots__ = ('opt_type')
opt_type = 'unicode' opt_type = 'unicode'
def _validate(self, value): def _validate(self, value):
return isinstance(value, unicode) return isinstance(value, unicode)
class SymLinkOption(object): class SymLinkOption(object):
__slots__ = ('_name', 'opt')
opt_type = 'symlink' opt_type = 'symlink'
def __init__(self, name, path, opt): def __init__(self, name, path, opt):
self._name = name self._name = name
self.path = path
self.opt = opt self.opt = opt
def setoption(self, config, value): def setoption(self, config, value):
setattr(config._cfgimpl_get_toplevel(), self.path, value) context = config.cfgimpl_get_context()
path = context.cfgimpl_get_description().get_path_by_opt(self.opt)
setattr(context, path, value)
def __getattr__(self, name): def __getattr__(self, name):
if name in ('_name', 'path', 'opt', 'setoption'): if name in ('_name', 'opt', 'setoption'):
return self.__dict__[name] return object.__gettattr__(self, name)
else: else:
return getattr(self.opt, name) return getattr(self.opt, name)
class IPOption(Option): class IPOption(Option):
__slots__ = ('opt_type')
opt_type = 'ip' opt_type = 'ip'
def _validate(self, value): def _validate(self, value):
# by now the validation is nothing but a string, use IPy instead # by now the validation is nothing but a string, use IPy instead
return isinstance(value, str) return isinstance(value, str)
class NetmaskOption(Option): class NetmaskOption(Option):
__slots__ = ('opt_type')
opt_type = 'netmask' opt_type = 'netmask'
def _validate(self, value): def _validate(self, value):
# by now the validation is nothing but a string, use IPy instead # by now the validation is nothing but a string, use IPy instead
return isinstance(value, str) return isinstance(value, str)
class OptionDescription(BaseType, BaseInformation):
class OptionDescription(BaseInformation):
"""Config's schema (organisation, group) and container of Options""" """Config's schema (organisation, group) and container of Options"""
# the group_type is useful for filtering OptionDescriptions in a config __slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
group_type = groups.default '_properties', '_children')
def __init__(self, name, doc, children, requires=None):
def __init__(self, name, doc, children, requires=None, properties=()):
""" """
:param children: is a list of option descriptions (including :param children: is a list of option descriptions (including
``OptionDescription`` instances for nested namespaces). ``OptionDescription`` instances for nested namespaces).
@ -418,49 +381,55 @@ class OptionDescription(BaseType, BaseInformation):
if not valid_name(name): if not valid_name(name):
raise NameError("invalid name: {0} for option descr".format(name)) raise NameError("invalid name: {0} for option descr".format(name))
self._name = name self._name = name
self.doc = doc
self._children = children
self._requires = requires
self._build()
self.properties = [] # 'hidden', 'disabled'...
self.informations = {} self.informations = {}
self._cache_paths = {} self.set_information('doc', doc)
child_names = [child._name for child in children]
#better performance like this
valid_child = copy(child_names)
valid_child.sort()
old = None
for child in valid_child:
if child == old:
raise ConflictConfigError('duplicate option name: '
'{0}'.format(child))
old = child
self._children = (tuple(child_names), tuple(children))
validate_requires_arg(requires, self._name)
self._requires = requires
self._cache_paths = None
if not isinstance(properties, tuple):
raise ConfigError('invalid properties type {0} for {1},'
' must be a tuple'.format(type(properties), self._name))
self._properties = properties # 'hidden', 'disabled'...
# the group_type is useful for filtering OptionDescriptions in a config
self._group_type = groups.default
def getdoc(self): def getdoc(self):
return self.doc return self.get_information('doc')
def _build(self): def __getattr__(self, name):
for child in self._children: if name in self._children[0]:
setattr(self, child._name, child) return self._children[1][self._children[0].index(name)]
else:
def add_child(self, child): try:
"dynamically adds a configuration option" object.__getattr__(self, name)
#Nothing is static. Even the Mona Lisa is falling apart. except AttributeError:
for ch in self._children: raise AttributeError('unknown Option {} in OptionDescription {}'
if isinstance(ch, Option): ''.format(name, self._name))
if child._name == ch._name:
raise ConflictConfigError("existing option : {0}".format(
child._name))
self._children.append(child)
setattr(self, child._name, child)
def update_child(self, child):
"modification of an existing option"
# XXX : corresponds to the `redefine`, is it usefull
pass
def getkey(self, config): def getkey(self, config):
return tuple([child.getkey(getattr(config, child._name)) return tuple([child.getkey(getattr(config, child._name))
for child in self._children]) for child in self._children[1]])
def getpaths(self, include_groups=False, currpath=None): def getpaths(self, include_groups=False, currpath=None):
"""returns a list of all paths in self, recursively """returns a list of all paths in self, recursively
currpath should not be provided (helps with recursion) currpath should not be provided (helps with recursion)
""" """
#FIXME : cache
if currpath is None: if currpath is None:
currpath = [] currpath = []
paths = [] paths = []
for option in self._children: for option in self._children[1]:
attr = option._name attr = option._name
if attr.startswith('_cfgimpl'): if attr.startswith('_cfgimpl'):
continue continue
@ -475,23 +444,51 @@ class OptionDescription(BaseType, BaseInformation):
paths.append('.'.join(currpath + [attr])) paths.append('.'.join(currpath + [attr]))
return paths return paths
def build_cache(self, cache=None, currpath=None): def build_cache(self, cache_path=None, cache_option=None, currpath=None):
if currpath is None and self._cache_paths != {}: if currpath is None and self._cache_paths is not None:
return return
if currpath is None: if currpath is None:
save = True
currpath = [] currpath = []
if cache is None: else:
cache = self._cache_paths save = False
for option in self._children: if cache_path is None:
cache_path = []
cache_option = []
for option in self._children[1]:
attr = option._name attr = option._name
if attr.startswith('_cfgimpl'): if attr.startswith('_cfgimpl'):
continue continue
cache_option.append(option)
cache_path.append(str('.'.join(currpath + [attr])))
if isinstance(option, OptionDescription): if isinstance(option, OptionDescription):
currpath.append(attr) currpath.append(attr)
option.build_cache(cache, currpath) option.build_cache(cache_path, cache_option, currpath)
currpath.pop() currpath.pop()
else: if save:
cache[option] = str('.'.join(currpath + [attr])) #valid no duplicated option
valid_child = copy(cache_option)
valid_child.sort()
old = None
for child in valid_child:
if child == old:
raise ConflictConfigError('duplicate option: '
'{0}'.format(child))
old = child
self._cache_paths = (tuple(cache_option), tuple(cache_path))
def get_opt_by_path(self, path):
try:
return self._cache_paths[0][self._cache_paths[1].index(path)]
except ValueError:
raise NotFoundError('no option for path {}'.format(path))
def get_path_by_opt(self, opt):
try:
return self._cache_paths[1][self._cache_paths[0].index(opt)]
except ValueError:
raise NotFoundError('no option {} found'.format(opt))
# ____________________________________________________________ # ____________________________________________________________
def set_group_type(self, group_type): def set_group_type(self, group_type):
"""sets a given group object to an OptionDescription """sets a given group object to an OptionDescription
@ -499,20 +496,39 @@ class OptionDescription(BaseType, BaseInformation):
:param group_type: an instance of `GroupType` or `MasterGroupType` :param group_type: an instance of `GroupType` or `MasterGroupType`
that lives in `setting.groups` that lives in `setting.groups`
""" """
if self._group_type != groups.default:
ConfigError('cannot change group_type if already set '
'(old {}, new {})'.format(self._group_type, group_type))
if isinstance(group_type, groups.GroupType): if isinstance(group_type, groups.GroupType):
self.group_type = group_type self._group_type = group_type
if isinstance(group_type, groups.MasterGroupType): if isinstance(group_type, groups.MasterGroupType):
#if master (same name has group) is set
identical_master_child_name = False identical_master_child_name = False
for child in self._children: #for collect all slaves
slaves = []
master = None
for child in self._children[1]:
if isinstance(child, OptionDescription): if isinstance(child, OptionDescription):
raise ConfigError("master group {} shall not have " raise ConfigError("master group {} shall not have "
"a subgroup".format(self._name)) "a subgroup".format(self._name))
if not child.multi: if not child.multi:
raise ConfigError("not allowed option {0} in group {1}" raise ConfigError("not allowed option {0} in group {1}"
": this option is not a multi".format(child._name, ": this option is not a multi"
self._name)) "".format(child._name, self._name))
if child._name == self._name: if child._name == self._name:
identical_master_child_name = True identical_master_child_name = True
child.multitype = multitypes.master
master = child
else:
slaves.append(child)
if master is None:
raise ConfigError('master group with wrong master name for {}'
''.format(self._name))
master.master_slaves = tuple(slaves)
for child in self._children[1]:
if child != master:
child.master_slaves = master
child.multitype = multitypes.slave
if not identical_master_child_name: if not identical_master_child_name:
raise ConfigError("the master group: {} has not any " raise ConfigError("the master group: {} has not any "
"master child".format(self._name)) "master child".format(self._name))
@ -520,94 +536,83 @@ class OptionDescription(BaseType, BaseInformation):
raise ConfigError('not allowed group_type : {0}'.format(group_type)) raise ConfigError('not allowed group_type : {0}'.format(group_type))
def get_group_type(self): def get_group_type(self):
return self.group_type return self._group_type
# ____________________________________________________________
"actions API"
def hide(self):
super(OptionDescription, self).hide()
for child in self._children:
if isinstance(child, OptionDescription):
child.hide()
def show(self):
super(OptionDescription, self).show()
for child in self._children:
if isinstance(child, OptionDescription):
child.show()
def disable(self):
super(OptionDescription, self).disable()
for child in self._children:
if isinstance(child, OptionDescription):
child.disable()
def enable(self):
super(OptionDescription, self).enable()
for child in self._children:
if isinstance(child, OptionDescription):
child.enable()
# ____________________________________________________________
def validate_requires_arg(requires, name): def validate_requires_arg(requires, name):
"malformed requirements" "check malformed requirements"
config_action = [] if requires is not None:
config_action = {}
for req in requires: for req in requires:
if not type(req) == tuple and len(req) != 3: if not type(req) == tuple:
raise RequiresError("malformed requirements for option:" raise RequiresError("malformed requirements type for option:"
" {0}".format(name)) " {0}, must be a tuple".format(name))
if len(req) == 3:
action = req[2] action = req[2]
if action not in available_actions: inverse = False
elif len(req) == 4:
action = req[2]
inverse = req[3]
else:
raise RequiresError("malformed requirements for option: {0}" raise RequiresError("malformed requirements for option: {0}"
" unknown action: {1}".format(name, action)) " invalid len".format(name))
if reverse_actions[action] in config_action: if action in config_action:
if inverse != config_action[action]:
raise RequiresError("inconsistency in action types for option: {0}" raise RequiresError("inconsistency in action types for option: {0}"
" action: {1} in contradiction with {2}\n" " action: {1}".format(name, action))
" ({3})".format(name, action, else:
reverse_actions[action], requires)) config_action[action] = inverse
config_action.append(action)
def build_actions(requires):
def apply_requires(opt, config):
"carries out the jit (just in time requirements between options"
def build_actions(requires):
"action are hide, show, enable, disable..." "action are hide, show, enable, disable..."
trigger_actions = {} trigger_actions = {}
for require in requires: for require in requires:
action = require[2] action = require[2]
trigger_actions.setdefault(action, []).append(require) trigger_actions.setdefault(action, []).append(require)
return trigger_actions return trigger_actions
#for symlink
def apply_requires(opt, config, permissive=False):
"carries out the jit (just in time requirements between options"
if hasattr(opt, '_requires') and opt._requires is not None: if hasattr(opt, '_requires') and opt._requires is not None:
rootconfig = config._cfgimpl_get_toplevel()
validate_requires_arg(opt._requires, opt._name)
# filters the callbacks # filters the callbacks
setting = config.cfgimpl_get_settings()
trigger_actions = build_actions(opt._requires) trigger_actions = build_actions(opt._requires)
if isinstance(opt, OptionDescription):
optpath = config._cfgimpl_get_path() + '.' + opt._name
else:
optpath = config.cfgimpl_get_context().cfgimpl_get_description().get_path_by_opt(opt)
for requires in trigger_actions.values(): for requires in trigger_actions.values():
matches = False matches = False
for require in requires: for require in requires:
name, expected, action = require if len(require) == 3:
path = config._cfgimpl_get_path() + '.' + opt._name path, expected, action = require
if name.startswith(path): inverse = False
elif len(require) == 4:
path, expected, action, inverse = require
if path.startswith(optpath):
raise RequirementRecursionError("malformed requirements " raise RequirementRecursionError("malformed requirements "
"imbrication detected for option: '{0}' " "imbrication detected for option: '{0}' "
"with requirement on: '{1}'".format(path, name)) "with requirement on: '{1}'".format(optpath, path))
homeconfig, shortname = rootconfig.cfgimpl_get_home_by_path(name)
try: try:
value = homeconfig._getattr(shortname, permissive=True) value = config.cfgimpl_get_context()._getattr(path, force_permissive=True)
except PropertiesOptionError, err: except PropertiesOptionError, err:
properties = err.proptype properties = err.proptype
if permissive:
for perm in \
config._cfgimpl_context._cfgimpl_settings.permissive:
if perm in properties:
properties.remove(perm)
if properties != []:
raise NotFoundError("option '{0}' has requirement's property error: " raise NotFoundError("option '{0}' has requirement's property error: "
"{1} {2}".format(opt._name, name, properties)) "{1} {2}".format(opt._name, path, properties))
except Exception, err: except Exception, err:
raise NotFoundError("required option not found: " raise NotFoundError("required option not found: "
"{0}".format(name)) "{0}".format(path))
if value == expected: if value == expected:
getattr(opt, action)() #.hide() or show() or... if inverse:
# FIXME generic programming opt.property_launch(action, False) setting.del_property(action, opt)
else:
setting.add_property(action, opt)
matches = True matches = True
# no callback has been triggered, then just reverse the action #FIXME optimisation : fait un double break non ? voire un return
# no requirement has been triggered, then just reverse the action
if not matches: if not matches:
getattr(opt, reverse_actions[action])() if inverse:
setting.add_property(action, opt)
else:
setting.del_property(action, opt)

View file

@ -20,20 +20,25 @@
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
class _const: class _const:
"""convenient class that emulates a module """convenient class that emulates a module
and builds constants (that is, unique names)""" and builds constants (that is, unique names)"""
class ConstError(TypeError): pass class ConstError(TypeError):
pass
def __setattr__(self, name, value): def __setattr__(self, name, value):
if self.__dict__.has_key(name): if name in self.__dict__:
raise self.ConstError, "Can't rebind group (%s)"%name raise self.ConstError, "Can't rebind group ({})".format(name)
self.__dict__[name] = value self.__dict__[name] = value
def __delattr__(self, name): def __delattr__(self, name):
if self.__dict__.has_key(name): if name in self.__dict__:
raise self.ConstError, "Can't unbind group (%s)"%name raise self.ConstError, "Can't unbind group ({})".format(name)
raise NameError, name raise NameError(name)
# ____________________________________________________________ # ____________________________________________________________
class GroupModule(_const): class GroupModule(_const):
"emulates a module to manage unique group (OptionDescription) names" "emulates a module to manage unique group (OptionDescription) names"
@ -42,6 +47,7 @@ class GroupModule(_const):
*normal* means : groups that are not master *normal* means : groups that are not master
""" """
pass pass
class DefaultGroupType(GroupType): class DefaultGroupType(GroupType):
"""groups that are default (typically 'default')""" """groups that are default (typically 'default')"""
pass pass
@ -54,6 +60,7 @@ class GroupModule(_const):
# setting.groups (emulates a module) # setting.groups (emulates a module)
groups = GroupModule() groups = GroupModule()
def populate_groups(): def populate_groups():
"populates the available groups in the appropriate namespaces" "populates the available groups in the appropriate namespaces"
groups.master = groups.MasterGroupType('master') groups.master = groups.MasterGroupType('master')
@ -62,6 +69,8 @@ def populate_groups():
# names are in the module now # names are in the module now
populate_groups() populate_groups()
# ____________________________________________________________ # ____________________________________________________________
class OwnerModule(_const): class OwnerModule(_const):
"""emulates a module to manage unique owner names. """emulates a module to manage unique owner names.
@ -72,12 +81,14 @@ class OwnerModule(_const):
"""allowed owner names """allowed owner names
""" """
pass pass
class DefaultOwner(Owner): class DefaultOwner(Owner):
"""groups that are default (typically 'default')""" """groups that are default (typically 'default')"""
pass pass
# setting.owners (emulates a module) # setting.owners (emulates a module)
owners = OwnerModule() owners = OwnerModule()
def populate_owners(): def populate_owners():
"""populates the available owners in the appropriate namespaces """populates the available owners in the appropriate namespaces
@ -85,7 +96,8 @@ def populate_owners():
- 'default' is the config owner after init time - 'default' is the config owner after init time
""" """
setattr(owners, 'default', owners.DefaultOwner('default')) setattr(owners, 'default', owners.DefaultOwner('default'))
setattr(owners,'user', owners.Owner('user')) setattr(owners, 'user', owners.Owner('user'))
def add_owner(name): def add_owner(name):
""" """
:param name: the name of the new owner :param name: the name of the new owner
@ -96,18 +108,23 @@ def populate_owners():
# names are in the module now # names are in the module now
populate_owners() populate_owners()
class MultiTypeModule(_const): class MultiTypeModule(_const):
class MultiType(str): class MultiType(str):
pass pass
class DefaultMultiType(MultiType): class DefaultMultiType(MultiType):
pass pass
class MasterMultiType(MultiType): class MasterMultiType(MultiType):
pass pass
class SlaveMultiType(MultiType): class SlaveMultiType(MultiType):
pass pass
multitypes = MultiTypeModule() multitypes = MultiTypeModule()
def populate_multitypes(): def populate_multitypes():
setattr(multitypes, 'default', multitypes.DefaultMultiType('default')) setattr(multitypes, 'default', multitypes.DefaultMultiType('default'))
setattr(multitypes, 'master', multitypes.MasterMultiType('master')) setattr(multitypes, 'master', multitypes.MasterMultiType('master'))
@ -115,118 +132,88 @@ def populate_multitypes():
populate_multitypes() populate_multitypes()
#____________________________________________________________ #____________________________________________________________
class Setting(): class Setting(object):
"``Config()``'s configuration options" "``Config()``'s configuration options"
__slots__ = ('properties', 'permissives', 'owner')
def __init__(self):
# properties attribute: the name of a property enables this property # properties attribute: the name of a property enables this property
properties = ['hidden', 'disabled'] # key is None for global properties
# overrides the validations in the acces of the option values self.properties = {None: []} # ['hidden', 'disabled', 'mandatory', 'frozen', 'validator']}
permissive = [] # permissive properties
# a mandatory option must have a value that is not None self.permissives = {}
mandatory = True
frozen = True
# enables validation function for options if set
validator = False
# generic owner # generic owner
owner = owners.user self.owner = owners.user
# in order to freeze everything, not **only** the frozen options
everything_frozen = False
# enables at build time to raise an exception if the option's name
# has the name of a config's method
valid_opt_names = True
#____________________________________________________________ #____________________________________________________________
# properties methods # properties methods
def has_properties(self): def has_properties(self, opt=None):
"has properties means the Config's properties attribute is not empty" "has properties means the Config's properties attribute is not empty"
return bool(len(self.properties)) return bool(len(self.get_properties(opt)))
def get_properties(self, opt=None):
if opt is None:
default = []
else:
default = list(opt._properties)
return self.properties.get(opt, default)
def get_properties(self): def has_property(self, propname, opt=None):
return self.properties
def has_property(self, propname):
"""has property propname in the Config's properties attribute """has property propname in the Config's properties attribute
:param property: string wich is the name of the property""" :param property: string wich is the name of the property"""
return propname in self.properties return propname in self.get_properties(opt)
def enable_property(self, propname): def enable_property(self, propname):
"puts property propname in the Config's properties attribute" "puts property propname in the Config's properties attribute"
if propname not in self.properties: props = self.get_properties()
self.properties.append(propname) if propname not in props:
props.append(propname)
self.set_properties(props)
def disable_property(self, propname): def disable_property(self, propname):
"deletes property propname in the Config's properties attribute" "deletes property propname in the Config's properties attribute"
if self.has_property(propname): props = self.get_properties()
self.properties.remove(propname) if propname in props:
#____________________________________________________________ props.remove(propname)
def get_permissive(self): self.set_properties(props)
return self.permissive
def set_permissive(self, permissive): def set_properties(self, properties, opt=None):
"""save properties for specified opt
(never save properties if same has option properties)
"""
if opt is None:
self.properties[opt] = properties
else:
if opt._properties == properties:
if opt in self.properties:
del(self.properties[opt])
else:
self.properties[opt] = properties
def add_property(self, propname, opt):
properties = self.get_properties(opt)
if not propname in properties:
properties.append(propname)
self.set_properties(properties, opt)
def del_property(self, propname, opt):
properties = self.get_properties(opt)
if propname in properties:
properties.remove(propname)
self.set_properties(properties, opt)
#____________________________________________________________
def get_permissive(self, config=None):
return self.permissives.get(config, [])
def set_permissive(self, permissive, config=None):
if not isinstance(permissive, list): if not isinstance(permissive, list):
raise TypeError('permissive must be a list') raise TypeError('permissive must be a list')
self.permissive = permissive self.permissives[config] = permissive
#____________________________________________________________ #____________________________________________________________
# complete freeze methods
def freeze_everything(self):
"""everything is frozen, not only the option that are tagged "frozen"
"""
self.everything_frozen = True
def un_freeze_everything(self):
"""everything is frozen, not only the option that are tagged "frozen"
"""
self.everything_frozen = False
def is_frozen_for_everything(self):
"""frozen for a whole config (not only the options
that have been set to frozen)"""
return self.everything_frozen
#____________________________________________________________
def read_only(self):
"convenience method to freeze, hidde and disable"
self.freeze_everything()
self.freeze() # can be usefull...
self.disable_property('hidden')
self.enable_property('disabled')
self.mandatory = True
self.validator = True
def read_write(self):
"convenience method to freeze, hidde and disable"
self.un_freeze_everything()
self.freeze()
self.enable_property('hidden')
self.enable_property('disabled')
self.mandatory = False
self.validator = False
def non_mandatory(self):
"""mandatory at the Config level means that the Config raises an error
if a mandatory option is found"""
self.mandatory = False
def mandatory(self):
"""mandatory at the Config level means that the Config raises an error
if a mandatory option is found"""
self.mandatory = True
def is_mandatory(self):
"all mandatory Options shall have a value"
return self.mandatory
def freeze(self):
"cannot modify the frozen `Option`'s"
self.frozen = True
def unfreeze(self):
"can modify the Options that are frozen"
self.frozen = False
def is_frozen(self):
"freeze flag at Config level"
return self.frozen
def setowner(self, owner): def setowner(self, owner):
":param owner: sets the default value for owner at the Config level" ":param owner: sets the default value for owner at the Config level"
if not isinstance(owner, owners.Owner): if not isinstance(owner, owners.Owner):
@ -235,3 +222,24 @@ class Setting():
def getowner(self): def getowner(self):
return self.owner return self.owner
#____________________________________________________________
def read_only(self):
"convenience method to freeze, hidde and disable"
self.enable_property('everything_frozen')
self.enable_property('frozen') # can be usefull...
self.disable_property('hidden')
self.enable_property('disabled')
self.enable_property('mandatory')
self.enable_property('validator')
self.disable_property('permissive')
def read_write(self):
"convenience method to freeze, hidde and disable"
self.disable_property('everything_frozen')
self.enable_property('frozen') # can be usefull...
self.enable_property('hidden')
self.enable_property('disabled')
self.disable_property('mandatory')
self.disable_property('validator')
self.enable_property('permissive')

View file

@ -16,144 +16,123 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# The original `Config` design model is unproudly borrowed from
# the rough pypy's guys: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence
# ____________________________________________________________ # ____________________________________________________________
from tiramisu.error import NoValueReturned, MandatoryError, MultiTypeError, \ from tiramisu.error import NoValueReturned, MandatoryError, MultiTypeError, \
ConfigError ConfigError # , OptionValueError
from tiramisu.setting import owners, multitypes from tiramisu.setting import owners, multitypes
class Values(object): class Values(object):
__slots__ = ('values', 'context')
def __init__(self, context): def __init__(self, context):
""" """
Initializes the values's dict. Initializes the values's dict.
:param context: the context is the home config's values and properties :param context: the context is the home config's values
""" """
self.owners = {}
"Config's root indeed is in charge of the `Option()`'s values" "Config's root indeed is in charge of the `Option()`'s values"
self.values = {} self.values = {}
self.previous_values = {}
self.masters = {}
self.slaves = {}
self.context = context self.context = context
def _get_multitype(self, opt):
if opt in self.slaves:
# slave
multitype = multitypes.slave
elif opt in self.masters:
# master
multitype = multitypes.master
# FIXME : default value for a multi, we shall work on groups
else:
multitype = multitypes.default
return multitype
def _get_value(self, opt): def _get_value(self, opt):
"special case for the multis: they never return None" "special case for the multis: they never return None"
if opt not in self.values: if opt not in self.values:
if opt.is_multi(): if opt.is_multi():
multitype = self._get_multitype(opt) value = Multi(opt.getdefault(), self.context, opt)
value = Multi(opt.getdefault(), self.context, opt, multitype) if opt.multitype == multitypes.slave:
else: masterpath = self.context._cfgimpl_descr.get_path_by_opt(opt.master_slaves)
value = opt.getdefault()
if opt in self.slaves:
masterpath = self.context._cfgimpl_descr._cache_paths[self.slaves[opt]]
mastervalue = getattr(self.context, masterpath) mastervalue = getattr(self.context, masterpath)
masterlen = len(mastervalue) masterlen = len(mastervalue)
if len(value) < masterlen: if len(value) < masterlen:
for num in range(0, masterlen - len(value)): for num in range(0, masterlen - len(value)):
value.append(None, force=True) value.append(None, force=True)
else:
value = opt.getdefault()
return value return value
return self.values[opt] return self.values[opt][1]
def reset(self, opt): def reset(self, opt):
if opt in self.values: if opt in self.values:
self.set_previous_value(opt)
del(self.values[opt]) del(self.values[opt])
self.setowner(opt, owners.default)
def set_previous_value(self, opt):
if opt in self.values:
old_value = self.values[opt]
elif opt.is_multi():
old_value = []
else:
old_value = None
if type(old_value) == Multi:
self.previous_values[opt] = list(old_value)
else:
self.previous_values[opt] = old_value
def get_previous_value(self, opt):
if opt in self.previous_values:
prec_value = self.previous_values[opt]
elif opt.is_multi():
prec_value = []
else:
prec_value = None
return prec_value
def _is_empty(self, opt, value=None): def _is_empty(self, opt, value=None):
"convenience method to know if an option is empty" "convenience method to know if an option is empty"
if value is not None: #FIXME: buggy ?
return False #if value is not None:
if (not opt.is_multi() and value == None) or \ # return False
(opt.is_multi() and (value == [] or \ if (not opt.is_multi() and value is None) or \
(opt.is_multi() and (value == [] or
None in self._get_value(opt))): None in self._get_value(opt))):
return True return True
return False return False
def is_empty(self, opt): def is_empty(self, opt):
#FIXME that not empty ... just no value!
if opt not in self.values: if opt not in self.values:
return True return True
value = self.values[opt] value = self.values[opt][1]
if not opt.is_multi(): if not opt.is_multi():
if self._get_value(opt) == None: if self._get_value(opt) is None:
return True return True
return False return False
else: else:
value = list(value) value = list(value)
for val in value: for val in value:
if val != None: if val is not None:
return False return False
return True return True
def _test_mandatory(self, opt, value=None): def _test_mandatory(self, opt, value, force_properties=None):
# mandatory options setting = self.context.cfgimpl_get_settings()
mandatory = self.context._cfgimpl_settings.mandatory if force_properties is None:
if opt.is_mandatory() and mandatory: set_mandatory = setting.has_property('mandatory')
else:
set_mandatory = ('mandatory' in force_properties or
setting.has_property('mandatory'))
if setting.has_property('mandatory', opt) and set_mandatory:
if self._is_empty(opt, value) and \ if self._is_empty(opt, value) and \
opt.is_empty_by_default(): opt.is_empty_by_default():
raise MandatoryError("option: {0} is mandatory " raise MandatoryError("option: {0} is mandatory "
"and shall have a value".format(opt._name)) "and shall have a value".format(opt._name))
#empty value
if opt.is_multi():
for val in value:
if val == '':
raise MandatoryError("option: {0} is mandatory "
"and shall have not empty value".format(opt._name))
else:
if value == '':
raise MandatoryError("option: {0} is mandatory "
"and shall have not empty value".format(opt._name))
def fill_multi(self, opt, result): def fill_multi(self, opt, result):
"""fills a multi option with default and calculated values """fills a multi option with default and calculated values
""" """
value = self._get_value(opt)
if not isinstance(result, list): if not isinstance(result, list):
_result = [result] _result = [result]
else: else:
_result = result _result = result
multitype = self._get_multitype(opt) #multitype = self._get_multitype(opt)
return Multi(_result, self.context, opt, multitype) return Multi(_result, self.context, opt) # , multitype)
def __getitem__(self, opt): def __getitem__(self, opt):
return self._getitem(opt)
def _getitem(self, opt, force_properties=None):
# options with callbacks # options with callbacks
value = self._get_value(opt) value = self._get_value(opt)
if opt.has_callback(): if opt.has_callback():
if (not opt.is_frozen() or \ setting = self.context.cfgimpl_get_settings()
not opt.is_forced_on_freeze()) and \ if (not setting.has_property('frozen', opt) or
not opt.is_default_owner(self.context): (setting.has_property('frozen', opt) and
not setting.has_property('force_default_on_freeze', opt)
)) and not self.context.cfgimpl_get_values().is_default_owner(opt):
return self._get_value(opt) return self._get_value(opt)
try: try:
result = opt.getcallback_value( result = opt.getcallback_value(self.context)
self.context) except NoValueReturned:
except NoValueReturned, err:
pass pass
else: else:
if opt.is_multi(): if opt.is_multi():
@ -162,85 +141,94 @@ class Values(object):
# this result **shall not** be a list # this result **shall not** be a list
if isinstance(result, list): if isinstance(result, list):
raise ConfigError('invalid calculated value returned ' raise ConfigError('invalid calculated value returned '
'for option {0} : shall not be a list'.format(opt._name)) 'for option {0} : shall not be a list'
''.format(opt._name))
value = result value = result
if value != None and not opt.validate(value, if value is not None and \
self.context._cfgimpl_settings.validator): not opt.validate(value, setting.has_property('validator')):
raise ConfigError('invalid calculated value returned' raise ConfigError('invalid calculated value returned'
' for option {0}'.format(opt._name)) ' for option {0}'.format(opt._name))
# frozen and force default # frozen and force default
if not opt.has_callback() and opt.is_forced_on_freeze(): if not opt.has_callback() and self.context.cfgimpl_get_settings().has_property('force_default_on_freeze', opt):
value = opt.getdefault() value = opt.getdefault()
if opt.is_multi(): if opt.is_multi():
value = self.fill_multi(opt, value) value = self.fill_multi(opt, value)
self._test_mandatory(opt, value) self._test_mandatory(opt, value, force_properties)
return value return value
def __setitem__(self, opt, value): def __setitem__(self, opt, value):
if opt in self.masters: if opt.is_multi():
if opt.multitype == multitypes.master:
masterlen = len(value) masterlen = len(value)
for slave in self.masters[opt]: for slave in self.opt.master_slaves:
value_slave = self._get_value(slave) value_slave = self._get_value(slave)
if len(value_slave) > masterlen: if len(value_slave) > masterlen:
raise MultiTypeError("invalid len for the slave: {0}" raise MultiTypeError("invalid len for the slave: {0}"
" which has {1} as master".format(slave._name, " which has {1} as master".format(
opt._name)) slave._name, opt._name))
elif len(value_slave) < masterlen: elif len(value_slave) < masterlen:
for num in range(0, masterlen - len(value_slave)): for num in range(0, masterlen - len(value_slave)):
value_slave.append(None, force=True) value_slave.append(None, force=True)
elif opt in self.slaves: elif opt.multitype == multitypes.slave:
if len(self._get_value(self.slaves[opt])) != len(value): if len(self._get_value(self.opt.master_slaves)) != len(value):
raise MultiTypeError("invalid len for the slave: {0}" raise MultiTypeError("invalid len for the slave: {0}"
" which has {1} as master".format(opt._name, " which has {1} as master".format(
self.slaves[opt]._name)) opt._name, self.opt.master_slaves._name))
if opt.is_multi() and not isinstance(value, Multi): if not isinstance(value, Multi):
value = Multi(value, self.context, opt, multitypes.default) value = Multi(value, self.context, opt)
self.setitem(opt, value) self.setitem(opt, value)
def setitem(self, opt, value): def setitem(self, opt, value):
self.set_previous_value(opt)
if type(value) == list: if type(value) == list:
raise MultiTypeError("the type of the value {0} which is multi shall " raise MultiTypeError("the type of the value {0} which is multi shall "
"be Multi and not list".format(str(value))) "be Multi and not list".format(str(value)))
self.values[opt] = value self._test_mandatory(opt, value)
self.setowner(opt, self.context._cfgimpl_settings.getowner()) self.values[opt] = (self.context.cfgimpl_get_settings().getowner(), value)
def __contains__(self, opt): def __contains__(self, opt):
return opt in self.values return opt in self.values
#____________________________________________________________
def setowner(self, opt, owner):
if isinstance(owner, owners.Owner):
self.owners[opt] = owner
else:
raise OptionValueError("Bad owner: " + str(owner))
def getowner(self, opt): def getowner(self, opt):
return self.owners.get(opt, owners.default) return self.values.get(opt, (owners.default, None))[0]
def setowner(self, opt, owner):
if opt not in self.values:
raise ConfigError('no value for {} cannot change owner to {}'.format(opt))
if not isinstance(owner, owners.Owner):
raise TypeError("invalid generic owner {0}".format(str(owner)))
self.values[opt] = (owner, self.values[opt][1])
def is_default_owner(self, opt):
"""
:param config: *must* be only the **parent** config
(not the toplevel config)
:return: boolean
"""
return self.getowner(opt) == owners.default
# ____________________________________________________________ # ____________________________________________________________
# multi types # multi types
class Multi(list): class Multi(list):
"""multi options values container """multi options values container
that support item notation for the values of multi options""" that support item notation for the values of multi options"""
def __init__(self, lst, context, opt, multitype): __slots__ = ('opt', 'context')
def __init__(self, lst, context, opt):
""" """
:param lst: the Multi wraps a list value :param lst: the Multi wraps a list value
:param context: the home config that has the settings and the values :param context: the home config that has the values
:param opt: the option object that have this Multi value :param opt: the option object that have this Multi value
""" """
self.settings = context._cfgimpl_settings
self.opt = opt self.opt = opt
self.values = context._cfgimpl_values self.context = context
self.multitype = multitype
super(Multi, self).__init__(lst) super(Multi, self).__init__(lst)
if multitype == multitypes.master:
self.slaves = context._cfgimpl_values.masters[opt]
else:
self.slaves = None
def __setitem__(self, key, value): def __setitem__(self, key, value):
self._validate(value) self._validate(value)
self.values[self.opt] = self self.context.cfgimpl_get_values()[self.opt] = self
super(Multi, self).__setitem__(key, value) super(Multi, self).__setitem__(key, value)
def append(self, value, force=False): def append(self, value, force=False):
@ -248,20 +236,21 @@ class Multi(list):
only if the option is a master only if the option is a master
""" """
if not force: if not force:
if self.multitype == multitypes.slave: if self.opt.multitype == multitypes.slave:
raise MultiTypeError("cannot append a value on a multi option {0}" raise MultiTypeError("cannot append a value on a multi option {0}"
" which is a slave".format(self.opt._name)) " which is a slave".format(self.opt._name))
elif self.multitype == multitypes.master: elif self.opt.multitype == multitypes.master:
for slave in self.slaves: for slave in self.opt.master_slaves:
self.values[slave].append(None, force=True) self.context.cfgimpl_get_values()[slave].append(None, force=True)
self._validate(value) self._validate(value)
self.values.setitem(self.opt, self) self.context.cfgimpl_get_values().setitem(self.opt, self)
super(Multi, self).append(value) super(Multi, self).append(value)
def _validate(self, value): def _validate(self, value):
if value != None and not self.opt._validate(value): if value is not None and not self.opt._validate(value):
raise ConfigError("invalid value {0} " raise ConfigError("invalid value {0} "
"for option {1}".format(str(value), self.opt._name)) "for option {1}".format(str(value),
self.opt._name))
def pop(self, key, force=False): def pop(self, key, force=False):
"""the list value can be updated (poped) """the list value can be updated (poped)
@ -271,11 +260,11 @@ class Multi(list):
:return: the requested element :return: the requested element
""" """
if not force: if not force:
if self.multitype == multitypes.slave: if self.opt.multitype == multitypes.slave:
raise MultiTypeError("cannot append a value on a multi option {0}" raise MultiTypeError("cannot append a value on a multi option {0}"
" which is a slave".format(self.opt._name)) " which is a slave".format(self.opt._name))
elif self.multitype == multitypes.master: elif self.opt.multitype == multitypes.master:
for slave in self.slaves: for slave in self.opt.master_slaves:
self.values[slave].pop(key, force=True) self.context.cfgimpl_get_values()[slave].pop(key, force=True)
self.values.setitem(self.opt, self) self.context.cfgimpl_get_values().setitem(self.opt, self)
return super(Multi, self).pop(key) return super(Multi, self).pop(key)