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

View File

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

View File

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

View File

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

View File

@ -5,12 +5,13 @@ from py.test import raises
from tiramisu.config import *
from tiramisu.option import *
from tiramisu.error import *
from tiramisu.setting import owners
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)
objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std')
('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0)
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)
descr = OptionDescription("options", "", [s])
config = Config(descr)
print config.string[1]
config.string[1] = "titi"
print config.string[1]
def test_reset():
"if value is None, resets to default owner"
@ -51,10 +50,10 @@ def test_reset():
config = Config(descr)
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)
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():
s = StrOption("string", "", default=["string"], default_multi="string" , multi=True)
@ -63,13 +62,13 @@ def test_reset_with_multi():
# config.string = []
config.unwrap_from_path("string").reset(config)
assert config.string == ["string"]
assert config._cfgimpl_values.owners[s] == 'default'
assert config.cfgimpl_get_values().getowner(s) == 'default'
config.string = ["eggs", "spam", "foo"]
assert config._cfgimpl_values.owners[s] == 'user'
assert config.cfgimpl_get_values().getowner(s) == 'user'
config.string = []
config.unwrap_from_path("string").reset(config)
# assert config.string == ["string"]
assert config._cfgimpl_values.owners[s] == 'default'
assert config.cfgimpl_get_values().getowner(s) == 'default'
raises(ConfigError, "config.string = None")
def test_default_with_multi():
@ -127,13 +126,15 @@ def test_multi_with_requires():
s = StrOption("string", "", default=["string"], default_multi="string", multi=True)
intoption = IntOption('int', 'Test int option', default=0)
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])
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
raises(PropertiesOptionError, "config.str = ['a', 'b']")
assert stroption._is_hidden()
assert config.cfgimpl_get_settings().has_property('hidden', stroption)
def test__requires_with_inverted():
s = StrOption("string", "", default=["string"], multi=True)
@ -142,40 +143,41 @@ def test__requires_with_inverted():
requires=[('int', 1, 'hide', 'inverted')], multi=True)
descr = OptionDescription("options", "", [s, intoption, stroption])
config = Config(descr)
assert stroption._is_hidden() == False
assert not config.cfgimpl_get_settings().has_property('hidden', stroption)
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():
s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'hide')], multi=True)
requires=[('int', 1, 'hidden')], multi=True)
descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr])
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
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():
s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'hide')], multi=True)
requires=[('int', 1, 'hidden')], multi=True)
descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr])
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
try:
config.opt.str
except:
pass
assert stroption._is_hidden()
raises(PropertiesOptionError, 'config.opt.str')
assert config.cfgimpl_get_settings().has_property('hidden', stroption)
def test_apply_requires_with_disabled():
@ -183,43 +185,46 @@ def test_apply_requires_with_disabled():
intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'disable')], multi=True)
requires=[('int', 1, 'disabled')], multi=True)
descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr])
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
try:
config.opt.str
except:
pass
assert stroption._is_disabled()
raises(PropertiesOptionError, 'config.opt.str')
assert config.cfgimpl_get_settings().has_property('disabled', stroption)
def test_multi_with_requires_with_disabled_in_another_group():
s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=0)
descr = OptionDescription("options", "", [intoption])
stroption = StrOption('str', 'Test string option', default=["abc"],
requires=[('int', 1, 'disable')], multi=True)
requires=[('int', 1, 'disabled')], multi=True)
descr = OptionDescription("opt", "", [stroption])
descr2 = OptionDescription("opt2", "", [intoption, s, descr])
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
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():
s = StrOption("string", "", default=["string"], multi=True)
intoption = IntOption('int', 'Test int option', default=[0], multi=True)
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])
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]
raises(PropertiesOptionError, "config.str = ['a', 'b']")
assert stroption._is_hidden()
assert config.cfgimpl_get_settings().has_property('hidden', stroption)
def test_multi_with_bool():
s = BoolOption("bool", "", default=[False], multi=True)
@ -227,7 +232,7 @@ def test_multi_with_bool():
config = Config(descr)
assert descr.bool.multi == True
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]
def test_multi_with_bool_two():
@ -238,7 +243,7 @@ def test_multi_with_bool_two():
raises(ConfigError, "config.bool = True")
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])
config = Config(descr)
config.t1 = ["a", "b", "a", "b"]
@ -257,7 +262,7 @@ def test_dwim_set():
descr = OptionDescription("opt", "", [
OptionDescription("sub", "", [
BoolOption("b1", ""),
ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
ChoiceOption("c1", "", ('a', 'b', 'c'), 'a'),
BoolOption("d1", ""),
]),
BoolOption("b2", ""),
@ -287,13 +292,14 @@ def test_more_set():
assert config.int == 23
def test_set_with_hidden_option():
boolopt = BoolOption("a", "", default=False)
boolopt.hide()
boolopt = BoolOption("a", "", default=False, properties=(('hidden'),))
descr = OptionDescription("opt", "", [
OptionDescription("s1", "", [boolopt]),
IntOption("int", "", default=42)])
d = {'s1.a': True, 'int': 23}
config = Config(descr)
setting = config.cfgimpl_get_settings()
setting.read_write()
raises(PropertiesOptionError, "config.set(**d)")
def test_set_with_unknown_option():
@ -380,16 +386,17 @@ def test_access_by_get():
assert cfg.get('dummy') == False
def test_access_by_get_whith_hide():
b1 = BoolOption("b1", "")
b1.hide()
b1 = BoolOption("b1", "", properties=(('hidden'),))
descr = OptionDescription("opt", "", [
OptionDescription("sub", "", [
b1,
ChoiceOption("c1", "", ['a', 'b', 'c'], 'a'),
ChoiceOption("c1", "", ('a', 'b', 'c'), 'a'),
BoolOption("d1", ""),
]),
BoolOption("b2", ""),
BoolOption("d1", ""),
])
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 *
def make_description():
gcoption = ChoiceOption('name', 'GC name', ['ref', 'framework'], 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False)
# hidding dummy here
gcdummy.hide()
gcoption = ChoiceOption('name', 'GC name', ('ref', 'framework'), 'ref')
gcdummy = BoolOption('dummy', 'dummy', default=False, properties=(('hidden'),))
objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std')
('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc")
wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=[('gc.name', 'ref')])
requires=(('gc.name', 'ref', 'hidden'),))
wantframework_option = BoolOption('wantframework', 'Test requires',
default=False,
requires=[('gc.name', 'framework')])
requires=(('gc.name', 'framework', 'hidden'),))
# ____________________________________________________________
booloptiontwo = BoolOption('booltwo', 'Test boolean option two', default=False)
@ -38,20 +36,20 @@ 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)
objspaceoption = ChoiceOption('objspace', 'Object space',
['std', 'thunk'], 'std')
('std', 'thunk'), 'std')
booloption = BoolOption('bool', 'Test boolean option', default=True)
intoption = IntOption('int', 'Test int option', default=0)
floatoption = FloatOption('float', 'Test float option', default=2.3)
stroption = StrOption('str', 'Test string option', default="abc")
boolop = BoolOption('boolop', 'Test boolean option op', default=True)
wantref_option = BoolOption('wantref', 'Test requires', default=False,
requires=['boolop'])
requires=(('boolop', True, 'hidden'),))
wantframework_option = BoolOption('wantframework', 'Test requires',
default=False,
requires=['boolop'])
requires=(('boolop', True, 'hidden'),))
gcgroup = OptionDescription('gc', '', [gcoption, gcdummy, floatoption])
descr = OptionDescription('tiramisu', '', [gcgroup, booloption, objspaceoption,
@ -64,10 +62,10 @@ def test_freeze_whole_config():
descr = make_description_freeze()
conf = Config(descr)
settings = conf.cfgimpl_get_settings()
settings.freeze_everything()
settings.enable_property('everything_frozen')
assert conf.gc.dummy == False
raises(TypeError, "conf.gc.dummy = True")
settings.un_freeze_everything()
settings.disable_property('everything_frozen')
conf.gc.dummy = True
assert conf.gc.dummy == True
@ -75,8 +73,11 @@ def test_freeze_one_option():
"freeze an option "
descr = make_description_freeze()
conf = Config(descr)
setting = conf.cfgimpl_get_settings()
setting.read_write()
#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
raises(TypeError, "conf.gc.dummy = True")
@ -85,45 +86,48 @@ def test_frozen_value():
s = StrOption("string", "", default="string")
descr = OptionDescription("options", "", [s])
config = Config(descr)
settings = config.cfgimpl_get_settings()
settings.freeze()
s.freeze()
settings = config.cfgimpl_get_settings().enable_property('frozen')
config.cfgimpl_get_settings().add_property('frozen', s)
raises(TypeError, 'config.string = "egg"')
def test_freeze():
"freeze a whole configuration object"
descr = make_description()
conf = Config(descr)
settings = conf.cfgimpl_get_settings()
settings.freeze()
settings = conf.cfgimpl_get_settings().enable_property('frozen')
name = conf.unwrap_from_path("gc.name")
name.freeze()
conf.cfgimpl_get_settings().add_property('frozen', name)
raises(TypeError, "conf.gc.name = 'framework'")
# ____________________________________________________________
def test_is_hidden():
descr = make_description()
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
raises(PropertiesOptionError, "config.gc.dummy == False")
# getattr
raises(PropertiesOptionError, "config.gc.dummy")
# I want to access to this option anyway
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():
descr = make_description()
config = Config(descr)
setting = config.cfgimpl_get_settings()
setting.read_write()
gc = config.unwrap_from_path('gc')
dummy = config.unwrap_from_path('gc.dummy')
gc.hide()
config.cfgimpl_get_settings().add_property('hidden', gc)
raises(PropertiesOptionError, "config.gc.dummy")
assert gc._is_hidden()
assert config.cfgimpl_get_settings().has_property('hidden', gc)
raises(PropertiesOptionError, "config.gc.float")
# manually set the subconfigs to "show"
gc.show()
assert gc._is_hidden() == False
config.cfgimpl_get_settings().del_property('hidden', gc)
assert not config.cfgimpl_get_settings().has_property('hidden', gc)
assert config.gc.float == 2.3
#dummy est en hide
raises(PropertiesOptionError, "config.gc.dummy == False")
@ -131,17 +135,22 @@ def test_group_is_hidden():
def test_global_show():
descr = make_description()
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")
def test_with_many_subgroups():
descr = make_description()
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
config.gc.subgroup._cfgimpl_descr.booltwo.hide()
config.cfgimpl_get_settings().add_property('hidden', booltwo)
path = 'gc.subgroup.booltwo'
homeconfig, name = config.cfgimpl_get_home_by_path(path)
assert name == "booltwo"
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)
adresse_serveur_ntp = StrOption('serveur_ntp', "adresse serveur ntp", multi=True)
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é")
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")
owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
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")
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():
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")
owner = cfg._cfgimpl_context._cfgimpl_settings.getowner()
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")
assert opt.getowner(cfg) == owner
cfg._cfgimpl_context._cfgimpl_values.reset(opt)
assert opt.getowner(cfg) == owners.default
assert cfg.cfgimpl_get_values().getowner(opt) == owner
cfg.cfgimpl_get_values().reset(opt)
assert cfg.cfgimpl_get_values().getowner(opt) == owners.default
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
# 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,
# # we have to carry out a calculation
# 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
# not to disrupt it
# config.freeze()
callback=option.getcallback()
callback_params=option.getcallback_params()
if callback_params is None:
callback_params = {}
#callback, callback_params = option.getcallback()
#if callback_params is None:
# callback_params = {}
tcparams = {}
one_is_multi = False
len_multi = 0
@ -44,7 +45,7 @@ def carry_out_calculation(name, option, config):
if type(value) == tuple:
path, check_disabled = value
try:
opt_value = config._getattr(path, permissive=True)
opt_value = config._getattr(path, force_permissive=True)
opt = config.unwrap_from_path(path)
except PropertiesOptionError, err:
if check_disabled:
@ -52,12 +53,14 @@ def carry_out_calculation(name, option, config):
raise PropertiesOptionError(err, err.proptype)
is_multi = opt.is_multi()
if is_multi:
if opt_value != None:
if opt_value is not None:
len_value = len(opt_value)
if len_multi != 0 and len_multi != len_value:
raise ConflictConfigError('unable to carry out a calculation, '
'option values with multi types must have same length for: '
+ name)
raise ConflictConfigError('unable to carry out '
'a calculation, option '
'values with multi types'
' must have same length '
'for: ' + name)
len_multi = len_value
one_is_multi = True
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 couple in couples:
value, ismulti = couple
if ismulti and value != None:
if ismulti and value is not None:
if key == '':
params.append(value[incr])
else:
@ -104,6 +107,7 @@ def carry_out_calculation(name, option, config):
tcp[key] = couple[0]
return calculate(name, callback, params, tcp)
def calculate(name, callback, params, tcparams):
try:
# XXX not only creole...
@ -113,5 +117,5 @@ def calculate(name, callback, params, tcparams):
import traceback
traceback.print_exc()
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 whole pypy projet is under MIT licence
# ____________________________________________________________
from copy import copy
from inspect import getmembers, ismethod
from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
MandatoryError, MethodCallError, NoValueReturned)
#from inspect import getmembers, ismethod
from tiramisu.error import (PropertiesOptionError, NotFoundError,
AmbigousOptionError, NoMatchingOptionFound, MandatoryError)
from tiramisu.option import (OptionDescription, Option, SymLinkOption,
apply_requires)
from tiramisu.setting import groups, owners, Setting
apply_requires)
from tiramisu.setting import groups, Setting
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
: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 parent: parent's `Config`
:type parent: `Config`
:param context: the current root config
:type context: `Config`
"""
# main option description
self._cfgimpl_descr = descr
# sub option descriptions
self._cfgimpl_subconfigs = {}
self._cfgimpl_subconfigs = None
self._cfgimpl_parent = parent
self._cfgimpl_permissive = []
if context is None:
self._cfgimpl_context = self
else:
self._cfgimpl_context = context
if parent is None:
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()
self._cfgimpl_context = context
#self._cfgimpl_build(slots)
def _cfgimpl_build_all_paths(self):
self._cfgimpl_descr.build_cache()
def cfgimpl_get_context(self):
return self._cfgimpl_context
def cfgimpl_get_settings(self):
return self._cfgimpl_context._cfgimpl_settings
def cfgimpl_set_settings(self, settings):
if not isinstance(settings, Setting):
raise ConfigError("setting not allowed")
self._cfgimpl_context._cfgimpl_settings = settings
def cfgimpl_get_values(self):
return self._cfgimpl_context._cfgimpl_values
def cfgimpl_get_description(self):
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
def __setattr__(self, name, value):
@ -156,53 +72,44 @@ class Config(object):
#self.__dict__[name] = value
object.__setattr__(self, name, value)
return
self._setattr(name, value)
def _setattr(self, name, value, force_permissive=False):
if '.' in 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:
self._validate(name, getattr(self._cfgimpl_descr, name))
if name in self._cfgimpl_slots:
raise NameError("invalid name for the option:"
" {0}".format(name))
self._validate(name, getattr(self._cfgimpl_descr, name), force_permissive=force_permissive)
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"
apply_requires(opt_or_descr, self, permissive=permissive)
apply_requires(opt_or_descr, self)
if not isinstance(opt_or_descr, Option) and \
not isinstance(opt_or_descr, OptionDescription):
raise TypeError('Unexpected object: {0}'.format(repr(opt_or_descr)))
properties = set(copy(opt_or_descr.properties))
properties = properties & set(self._cfgimpl_context._cfgimpl_settings.get_properties())
if permissive:
properties = properties - set(self._cfgimpl_context._cfgimpl_settings.get_permissive())
properties = properties - set(self._cfgimpl_permissive)
properties = set(self.cfgimpl_get_settings().get_properties(opt_or_descr))
#remove this properties, those properties are validate in value/setting
properties = properties - set(['mandatory', 'frozen'])
set_properties = set(self.cfgimpl_get_settings().get_properties())
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)
if properties != []:
raise PropertiesOptionError("trying to access"
" to an option named: {0} with properties"
" {1}".format(name, str(properties)),
properties)
" to an option named: {0} with properties"
" {1}".format(name, str(properties)),
properties)
def __getattr__(self, name):
return self._getattr(name)
# def fill_multi(self, opt, result, use_default_multi=False, default_multi=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):
def _getattr(self, name, force_permissive=False, force_properties=None):
"""
attribute notation mechanism for accessing the value of an option
: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
otherwise
"""
@ -210,38 +117,222 @@ class Config(object):
# for instance getattr(self, "creole.general.family.adresse_ip_eth0")
if '.' in 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)
# symlink options
if type(opt_or_descr) == SymLinkOption:
rootconfig = self._cfgimpl_get_toplevel()
return getattr(rootconfig, opt_or_descr.path)
rootconfig = self.cfgimpl_get_context()
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 opt_or_descr not in self._cfgimpl_subconfigs:
raise AttributeError("%s with name %s object has no attribute %s" %
(self.__class__, opt_or_descr._name, name))
return self._cfgimpl_subconfigs[opt_or_descr]
children = self.cfgimpl_get_description()._children
if opt_or_descr not in children[1]:
raise AttributeError("{0} with name {1} object has "
"no attribute {2}".format(self.__class__,
opt_or_descr._name,
name))
return SubConfig(opt_or_descr, self, self._cfgimpl_context)
# special attributes
if name.startswith('_cfgimpl_'):
# if it were in __dict__ it would have been found already
return self.__dict__[name]
return self._cfgimpl_context._cfgimpl_values[opt_or_descr]
object.__getattr__(self, name)
return self.cfgimpl_get_values()._getitem(opt_or_descr,
force_properties=force_properties)
def unwrap_from_name(self, name):
"""convenience method to extract and Option() object from the Config()
**and it is slow**: it recursively searches into the namespaces
:returns: Option()
def setoption(self, name, value, who=None):
"""effectively modifies the value of an Option()
(typically called by the __setattr__)
"""
paths = self.getpaths(allpaths=True)
opts = dict([(path, self.unwrap_from_path(path)) for path in paths])
all_paths = [p.split(".") for p in self.getpaths()]
for pth in all_paths:
if name in pth:
return opts[".".join(pth)]
raise NotFoundError("name: {0} not found".format(name))
child = getattr(self._cfgimpl_descr, name)
child.setoption(self, value)
def cfgimpl_get_home_by_path(self, path):
""":returns: tuple (config, name)"""
path = path.split('.')
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):
"""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(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):
"""
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'
' 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:
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
the search for the option is performed recursively in the whole
configuration tree.
**carefull**: very slow !
:returns: option value.
"""
paths = self.getpaths(allpaths=True)
pathsvalues = []
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))
return self._find(byname=name, bytype=None, byvalue=None, byattrs=None,
first=True, getvalue=True, _subpath=_subpath)
def cfgimpl_get_home_by_path(self, path):
""":returns: tuple (config, name)"""
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):
def _find(self, bytype, byname, byvalue, byattrs, first, getvalue=False,
_subpath=None):
"""
convenience method for finding an option that lives only in the subtree
:param first: return only one option if True, a list otherwise
: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():
if byattrs is None:
return True
@ -487,37 +436,15 @@ class Config(object):
else:
continue
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 = []
paths = self.getpaths(allpaths=True)
for path in paths:
try:
option = self.unwrap_from_path(path)
except PropertiesOptionError, err:
opts, paths = self.cfgimpl_get_description()._cache_paths
for index in range(0, len(paths)):
path = paths[index]
option = opts[index]
if isinstance(option, OptionDescription):
continue
if _subpath is not None and not path.startswith(_subpath + '.'):
continue
if not _filter_by_name():
continue
@ -527,17 +454,27 @@ class Config(object):
continue
if not _filter_by_attrs():
continue
#remove option with propertyerror, ...
try:
value = getattr(self, path)
except: # a property restricts the access of the value
continue
if first:
return option
if getvalue:
return value
else:
return option
else:
find_results.append(option)
if getvalue:
find_results.append(value)
else:
find_results.append(option)
if find_results == []:
raise NotFoundError("no option found in config with these criteria")
else:
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
@ -547,9 +484,9 @@ class Config(object):
:param byattrs: dict of option attributes (default, callback...)
: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
@ -559,7 +496,7 @@ class Config(object):
:param byattrs: dict of option attributes (default, callback...)
: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):
@ -586,15 +523,11 @@ def mandatory_warnings(config):
where no value has been set
:returns: generator of mandatory Option's path
FIXME : CAREFULL : not multi-user
"""
mandatory = config._cfgimpl_context._cfgimpl_settings.mandatory
config._cfgimpl_context._cfgimpl_settings.mandatory = True
for path in config._cfgimpl_descr.getpaths(include_groups=True):
for path in config.cfgimpl_get_description().getpaths(include_groups=True):
try:
value = config._getattr(path, permissive=True)
config._getattr(path, force_properties=('mandatory',))
except MandatoryError:
yield path
except PropertiesOptionError:
pass
config._cfgimpl_context._cfgimpl_settings.mandatory = mandatory

View File

@ -21,26 +21,17 @@
# the whole pypy projet is under MIT licence
# ____________________________________________________________
import re
from copy import copy
from types import FunctionType
from tiramisu.basetype import BaseType
from tiramisu.error import (ConfigError, ConflictConfigError, NotFoundError,
RequiresError, RequirementRecursionError, MandatoryError,
PropertiesOptionError)
from tiramisu.error import (ConfigError, NotFoundError, ConflictConfigError,
RequiresError, RequirementRecursionError,
PropertiesOptionError)
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+')
def valid_name(name):
try:
name = str(name)
@ -53,7 +44,9 @@ def valid_name(name):
#____________________________________________________________
#
class BaseInformation:
class BaseInformation(object):
__slots__ = ('informations')
def set_information(self, key, value):
"""updates the information's attribute
@ -64,29 +57,33 @@ class BaseInformation:
"""
self.informations[key] = value
def get_information(self, key):
def get_information(self, key, default=None):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
if key in self.informations:
return self.informations[key]
elif default is not None:
return default
else:
raise ValueError("Information's item not found: {0}".format(key))
class Option(BaseType, BaseInformation):
class Option(BaseInformation):
"""
Abstract base class for configuration option's.
Reminder: an Option object is **not** a container for the value
"""
#freeze means: cannot modify the value of an Option once set
_frozen = False
#if an Option has been frozen, shall return the default value
_force_default_on_freeze = False
__slots__ = ('_name', '_requires', 'multi', '_validator', 'default_multi',
'default', '_properties', 'callback', 'multitype',
'master_slaves')
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, mandatory=False, multi=False, callback=None,
callback_params=None, validator=None, validator_args={}):
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_args=None,
properties=None):
"""
:param name: the option's name
:param doc: the option's description
@ -107,51 +104,58 @@ class Option(BaseType, BaseInformation):
if not valid_name(name):
raise NameError("invalid name: {0} for option".format(name))
self._name = name
self.doc = doc
self.informations = {}
self.set_information('doc', doc)
validate_requires_arg(requires, self._name)
self._requires = requires
self._mandatory = mandatory
self.multi = multi
self._validator = None
self._validator_args = None
#self._validator_args = None
if validator is not None:
if type(validator) != FunctionType:
raise TypeError("validator must be a function")
self._validator = validator
if validator_args is not None:
self._validator_args = validator_args
self._validator = (validator, validator_args)
else:
self._validator = None
if not self.multi and default_multi is not None:
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):
raise ConfigError("invalid default_multi value {0} "
"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
"for option {1}".format(str(default_multi), name))
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} "
"is calculated".format(name))
self.callback = callback
if self.callback is None and callback_params is not None:
raise ConfigError("params defined for a callback function but"
" no callback defined yet for option {0}".format(name))
self.callback_params = callback_params
if self.multi == True:
if default == None:
"is calculated".format(name))
if callback is None and callback_params is not None:
raise ConfigError("params defined for a callback function but "
"no callback defined yet for option {0}".format(name))
if callback is not None:
self.callback = (callback, callback_params)
else:
self.callback = None
if self.multi:
if default is None:
default = []
if not isinstance(default, list):
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):
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:
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} "
"for option {1}".format(str(default), name))
"for option {1}".format(str(default), name))
self.default = default
self.properties = [] # 'hidden', 'disabled'...
self.informations = {}
if properties is None:
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):
"""
@ -159,25 +163,25 @@ class Option(BaseType, BaseInformation):
:param validate: if true enables ``self._validator`` validation
"""
# generic calculation
if self.multi == False:
if not self.multi:
# None allows the reset of the value
if value != None:
if value is not None:
# customizing the validator
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 self._validate(value)
else:
if not isinstance(value, list):
raise ConfigError("invalid value {0} "
"for option {1} which must be a list".format(value,
self._name))
"for option {1} which must be a list"
"".format(value, self._name))
for val in value:
# None allows the reset of the value
if val != None:
if val is not None:
# customizing the validator
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
if not self._validate(val):
return False
@ -185,7 +189,7 @@ class Option(BaseType, BaseInformation):
def getdefault(self, default_multi=False):
"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
else:
return self.getdefault_multi()
@ -196,148 +200,88 @@ class Option(BaseType, BaseInformation):
def is_empty_by_default(self):
"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))):
return True
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):
"accesses the Option's doc"
return self.doc
def getcallback(self):
"a callback is only a link, the name of an external hook"
return self.callback
return self.get_information('doc')
def has_callback(self):
"to know if a callback has been defined or not"
if self.callback == None:
if self.callback is None:
return False
else:
return True
def getcallback_value(self, config):
return carry_out_calculation(self._name,
option=self, config=config)
def getcallback_params(self):
"if a callback has been defined, returns his arity"
return self.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)
callback, callback_params = self.callback
if callback_params is None:
callback_params = {}
return carry_out_calculation(self._name, config=config,
callback=callback,
callback_params=callback_params)
def reset(self, config):
"""resets the default value and owner
"""
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):
"""changes the option's value with the value_owner's who
:param config: the parent config is necessary here to store the value
"""
name = self._name
rootconfig = config._cfgimpl_get_toplevel()
if not self.validate(value,
config._cfgimpl_context._cfgimpl_settings.validator):
setting = config.cfgimpl_get_settings()
if not self.validate(value, setting.has_property('validator')):
raise ConfigError('invalid value %s for option %s' % (value, name))
if self.is_mandatory():
# 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:
if self not in config._cfgimpl_descr._children[1]:
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 "
"config has been frozen".format(name))
"config has been frozen".format(name))
if config._cfgimpl_context._cfgimpl_settings.is_frozen() \
and self.is_frozen():
if setting.has_property('frozen') and setting.has_property('frozen',
self):
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)
config._cfgimpl_context._cfgimpl_values[self] = value
config.cfgimpl_get_values()[self] = value
def getkey(self, 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):
return self.multi
def is_mandatory(self):
return self._mandatory
class ChoiceOption(Option):
__slots__ = ('values', 'open_values', 'opt_type')
opt_type = 'string'
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,
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
if open_values not in [True, False]:
if open_values not in (True, False):
raise ConfigError('Open_values must be a boolean for '
'{0}'.format(name))
self.open_values = open_values
super(ChoiceOption, self).__init__(name, doc, default=default,
default_multi=default_multi, callback=callback,
callback_params=callback_params, requires=requires,
multi=multi, mandatory=mandatory, validator=validator,
validator_args=validator_args)
default_multi=default_multi,
callback=callback,
callback_params=callback_params,
requires=requires,
multi=multi,
validator=validator,
validator_args=validator_args,
properties=properties)
def _validate(self, value):
if not self.open_values:
@ -345,72 +289,91 @@ class ChoiceOption(Option):
else:
return True
class BoolOption(Option):
__slots__ = ('opt_type')
opt_type = 'bool'
def _validate(self, value):
return isinstance(value, bool)
class IntOption(Option):
__slots__ = ('opt_type')
opt_type = 'int'
def _validate(self, value):
return isinstance(value, int)
class FloatOption(Option):
__slots__ = ('opt_type')
opt_type = 'float'
def _validate(self, value):
return isinstance(value, float)
class StrOption(Option):
__slots__ = ('opt_type')
opt_type = 'string'
def _validate(self, value):
return isinstance(value, str)
class UnicodeOption(Option):
__slots__ = ('opt_type')
opt_type = 'unicode'
def _validate(self, value):
return isinstance(value, unicode)
class SymLinkOption(object):
__slots__ = ('_name', 'opt')
opt_type = 'symlink'
def __init__(self, name, path, opt):
self._name = name
self.path = path
self.opt = opt
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):
if name in ('_name', 'path', 'opt', 'setoption'):
return self.__dict__[name]
if name in ('_name', 'opt', 'setoption'):
return object.__gettattr__(self, name)
else:
return getattr(self.opt, name)
class IPOption(Option):
__slots__ = ('opt_type')
opt_type = 'ip'
def _validate(self, value):
# by now the validation is nothing but a string, use IPy instead
return isinstance(value, str)
class NetmaskOption(Option):
__slots__ = ('opt_type')
opt_type = 'netmask'
def _validate(self, value):
# by now the validation is nothing but a string, use IPy instead
return isinstance(value, str)
class OptionDescription(BaseType, BaseInformation):
class OptionDescription(BaseInformation):
"""Config's schema (organisation, group) and container of Options"""
# the group_type is useful for filtering OptionDescriptions in a config
group_type = groups.default
def __init__(self, name, doc, children, requires=None):
__slots__ = ('_name', '_requires', '_cache_paths', '_group_type',
'_properties', '_children')
def __init__(self, name, doc, children, requires=None, properties=()):
"""
:param children: is a list of option descriptions (including
``OptionDescription`` instances for nested namespaces).
@ -418,49 +381,55 @@ class OptionDescription(BaseType, BaseInformation):
if not valid_name(name):
raise NameError("invalid name: {0} for option descr".format(name))
self._name = name
self.doc = doc
self._children = children
self._requires = requires
self._build()
self.properties = [] # 'hidden', 'disabled'...
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):
return self.doc
return self.get_information('doc')
def _build(self):
for child in self._children:
setattr(self, child._name, child)
def add_child(self, child):
"dynamically adds a configuration option"
#Nothing is static. Even the Mona Lisa is falling apart.
for ch in self._children:
if isinstance(ch, Option):
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 __getattr__(self, name):
if name in self._children[0]:
return self._children[1][self._children[0].index(name)]
else:
try:
object.__getattr__(self, name)
except AttributeError:
raise AttributeError('unknown Option {} in OptionDescription {}'
''.format(name, self._name))
def getkey(self, config):
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):
"""returns a list of all paths in self, recursively
currpath should not be provided (helps with recursion)
"""
#FIXME : cache
if currpath is None:
currpath = []
paths = []
for option in self._children:
for option in self._children[1]:
attr = option._name
if attr.startswith('_cfgimpl'):
continue
@ -469,29 +438,57 @@ class OptionDescription(BaseType, BaseInformation):
paths.append('.'.join(currpath + [attr]))
currpath.append(attr)
paths += option.getpaths(include_groups=include_groups,
currpath=currpath)
currpath=currpath)
currpath.pop()
else:
paths.append('.'.join(currpath + [attr]))
return paths
def build_cache(self, cache=None, currpath=None):
if currpath is None and self._cache_paths != {}:
def build_cache(self, cache_path=None, cache_option=None, currpath=None):
if currpath is None and self._cache_paths is not None:
return
if currpath is None:
save = True
currpath = []
if cache is None:
cache = self._cache_paths
for option in self._children:
else:
save = False
if cache_path is None:
cache_path = []
cache_option = []
for option in self._children[1]:
attr = option._name
if attr.startswith('_cfgimpl'):
continue
cache_option.append(option)
cache_path.append(str('.'.join(currpath + [attr])))
if isinstance(option, OptionDescription):
currpath.append(attr)
option.build_cache(cache, currpath)
option.build_cache(cache_path, cache_option, currpath)
currpath.pop()
else:
cache[option] = str('.'.join(currpath + [attr]))
if save:
#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):
"""sets a given group object to an OptionDescription
@ -499,115 +496,123 @@ class OptionDescription(BaseType, BaseInformation):
:param group_type: an instance of `GroupType` or `MasterGroupType`
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):
self.group_type = group_type
self._group_type = group_type
if isinstance(group_type, groups.MasterGroupType):
#if master (same name has group) is set
identical_master_child_name = False
for child in self._children:
#for collect all slaves
slaves = []
master = None
for child in self._children[1]:
if isinstance(child, OptionDescription):
raise ConfigError("master group {} shall not have "
"a subgroup".format(self._name))
"a subgroup".format(self._name))
if not child.multi:
raise ConfigError("not allowed option {0} in group {1}"
": this option is not a multi".format(child._name,
self._name))
": this option is not a multi"
"".format(child._name, self._name))
if child._name == self._name:
identical_master_child_name = True
child.multitype = multitypes.master
master = child
else:
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:
raise ConfigError("the master group: {} has not any "
"master child".format(self._name))
"master child".format(self._name))
else:
raise ConfigError('not allowed group_type : {0}'.format(group_type))
def get_group_type(self):
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()
return self._group_type
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):
"malformed requirements"
config_action = []
for req in requires:
if not type(req) == tuple and len(req) != 3:
raise RequiresError("malformed requirements for option:"
" {0}".format(name))
action = req[2]
if action not in available_actions:
raise RequiresError("malformed requirements for option: {0}"
" unknown action: {1}".format(name, action))
if reverse_actions[action] in config_action:
raise RequiresError("inconsistency in action types for option: {0}"
" action: {1} in contradiction with {2}\n"
" ({3})".format(name, action,
reverse_actions[action], requires))
config_action.append(action)
"check malformed requirements"
if requires is not None:
config_action = {}
for req in requires:
if not type(req) == tuple:
raise RequiresError("malformed requirements type for option:"
" {0}, must be a tuple".format(name))
if len(req) == 3:
action = req[2]
inverse = False
elif len(req) == 4:
action = req[2]
inverse = req[3]
else:
raise RequiresError("malformed requirements for option: {0}"
" invalid len".format(name))
if action in config_action:
if inverse != config_action[action]:
raise RequiresError("inconsistency in action types for option: {0}"
" action: {1}".format(name, action))
else:
config_action[action] = inverse
def build_actions(requires):
"action are hide, show, enable, disable..."
trigger_actions = {}
for require in requires:
action = require[2]
trigger_actions.setdefault(action, []).append(require)
return trigger_actions
def apply_requires(opt, config, permissive=False):
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..."
trigger_actions = {}
for require in requires:
action = require[2]
trigger_actions.setdefault(action, []).append(require)
return trigger_actions
#for symlink
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
setting = config.cfgimpl_get_settings()
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():
matches = False
for require in requires:
name, expected, action = require
path = config._cfgimpl_get_path() + '.' + opt._name
if name.startswith(path):
if len(require) == 3:
path, expected, action = require
inverse = False
elif len(require) == 4:
path, expected, action, inverse = require
if path.startswith(optpath):
raise RequirementRecursionError("malformed requirements "
"imbrication detected for option: '{0}' "
"with requirement on: '{1}'".format(path, name))
homeconfig, shortname = rootconfig.cfgimpl_get_home_by_path(name)
"imbrication detected for option: '{0}' "
"with requirement on: '{1}'".format(optpath, path))
try:
value = homeconfig._getattr(shortname, permissive=True)
value = config.cfgimpl_get_context()._getattr(path, force_permissive=True)
except PropertiesOptionError, err:
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: "
"{1} {2}".format(opt._name, name, properties))
raise NotFoundError("option '{0}' has requirement's property error: "
"{1} {2}".format(opt._name, path, properties))
except Exception, err:
raise NotFoundError("required option not found: "
"{0}".format(name))
"{0}".format(path))
if value == expected:
getattr(opt, action)() #.hide() or show() or...
# FIXME generic programming opt.property_launch(action, False)
if inverse:
setting.del_property(action, opt)
else:
setting.add_property(action, opt)
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:
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 whole pypy projet is under MIT licence
# ____________________________________________________________
class _const:
"""convenient class that emulates a module
and builds constants (that is, unique names)"""
class ConstError(TypeError): pass
class ConstError(TypeError):
pass
def __setattr__(self, name, value):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't rebind group (%s)"%name
if name in self.__dict__:
raise self.ConstError, "Can't rebind group ({})".format(name)
self.__dict__[name] = value
def __delattr__(self, name):
if self.__dict__.has_key(name):
raise self.ConstError, "Can't unbind group (%s)"%name
raise NameError, name
if name in self.__dict__:
raise self.ConstError, "Can't unbind group ({})".format(name)
raise NameError(name)
# ____________________________________________________________
class GroupModule(_const):
"emulates a module to manage unique group (OptionDescription) names"
@ -42,6 +47,7 @@ class GroupModule(_const):
*normal* means : groups that are not master
"""
pass
class DefaultGroupType(GroupType):
"""groups that are default (typically 'default')"""
pass
@ -54,6 +60,7 @@ class GroupModule(_const):
# setting.groups (emulates a module)
groups = GroupModule()
def populate_groups():
"populates the available groups in the appropriate namespaces"
groups.master = groups.MasterGroupType('master')
@ -62,6 +69,8 @@ def populate_groups():
# names are in the module now
populate_groups()
# ____________________________________________________________
class OwnerModule(_const):
"""emulates a module to manage unique owner names.
@ -72,12 +81,14 @@ class OwnerModule(_const):
"""allowed owner names
"""
pass
class DefaultOwner(Owner):
"""groups that are default (typically 'default')"""
pass
# setting.owners (emulates a module)
owners = OwnerModule()
def populate_owners():
"""populates the available owners in the appropriate namespaces
@ -85,7 +96,8 @@ def populate_owners():
- 'default' is the config owner after init time
"""
setattr(owners, 'default', owners.DefaultOwner('default'))
setattr(owners,'user', owners.Owner('user'))
setattr(owners, 'user', owners.Owner('user'))
def add_owner(name):
"""
:param name: the name of the new owner
@ -96,18 +108,23 @@ def populate_owners():
# names are in the module now
populate_owners()
class MultiTypeModule(_const):
class MultiType(str):
pass
class DefaultMultiType(MultiType):
pass
class MasterMultiType(MultiType):
pass
class SlaveMultiType(MultiType):
pass
multitypes = MultiTypeModule()
def populate_multitypes():
setattr(multitypes, 'default', multitypes.DefaultMultiType('default'))
setattr(multitypes, 'master', multitypes.MasterMultiType('master'))
@ -115,118 +132,88 @@ def populate_multitypes():
populate_multitypes()
#____________________________________________________________
class Setting():
class Setting(object):
"``Config()``'s configuration options"
# properties attribute: the name of a property enables this property
properties = ['hidden', 'disabled']
# overrides the validations in the acces of the option values
permissive = []
# a mandatory option must have a value that is not None
mandatory = True
frozen = True
# enables validation function for options if set
validator = False
# generic owner
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
__slots__ = ('properties', 'permissives', 'owner')
def __init__(self):
# properties attribute: the name of a property enables this property
# key is None for global properties
self.properties = {None: []} # ['hidden', 'disabled', 'mandatory', 'frozen', 'validator']}
# permissive properties
self.permissives = {}
# generic owner
self.owner = owners.user
#____________________________________________________________
# properties methods
def has_properties(self):
def has_properties(self, opt=None):
"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):
return self.properties
def has_property(self, propname):
def has_property(self, propname, opt=None):
"""has property propname in the Config's properties attribute
: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):
"puts property propname in the Config's properties attribute"
if propname not in self.properties:
self.properties.append(propname)
props = self.get_properties()
if propname not in props:
props.append(propname)
self.set_properties(props)
def disable_property(self, propname):
"deletes property propname in the Config's properties attribute"
if self.has_property(propname):
self.properties.remove(propname)
#____________________________________________________________
def get_permissive(self):
return self.permissive
props = self.get_properties()
if propname in props:
props.remove(propname)
self.set_properties(props)
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):
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):
":param owner: sets the default value for owner at the Config level"
if not isinstance(owner, owners.Owner):
@ -235,3 +222,24 @@ class Setting():
def getowner(self):
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
# 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, \
ConfigError
ConfigError # , OptionValueError
from tiramisu.setting import owners, multitypes
class Values(object):
__slots__ = ('values', 'context')
def __init__(self, context):
"""
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"
self.values = {}
self.previous_values = {}
self.masters = {}
self.slaves = {}
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):
"special case for the multis: they never return None"
if opt not in self.values:
if opt.is_multi():
multitype = self._get_multitype(opt)
value = Multi(opt.getdefault(), self.context, opt, multitype)
value = Multi(opt.getdefault(), self.context, opt)
if opt.multitype == multitypes.slave:
masterpath = self.context._cfgimpl_descr.get_path_by_opt(opt.master_slaves)
mastervalue = getattr(self.context, masterpath)
masterlen = len(mastervalue)
if len(value) < masterlen:
for num in range(0, masterlen - len(value)):
value.append(None, force=True)
else:
value = opt.getdefault()
if opt in self.slaves:
masterpath = self.context._cfgimpl_descr._cache_paths[self.slaves[opt]]
mastervalue = getattr(self.context, masterpath)
masterlen = len(mastervalue)
if len(value) < masterlen:
for num in range(0, masterlen - len(value)):
value.append(None, force=True)
return value
return self.values[opt]
return self.values[opt][1]
def reset(self, opt):
if opt in self.values:
self.set_previous_value(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):
"convenience method to know if an option is empty"
if value is not None:
return False
if (not opt.is_multi() and value == None) or \
(opt.is_multi() and (value == [] or \
None in self._get_value(opt))):
#FIXME: buggy ?
#if value is not None:
# return False
if (not opt.is_multi() and value is None) or \
(opt.is_multi() and (value == [] or
None in self._get_value(opt))):
return True
return False
def is_empty(self, opt):
#FIXME that not empty ... just no value!
if opt not in self.values:
return True
value = self.values[opt]
value = self.values[opt][1]
if not opt.is_multi():
if self._get_value(opt) == None:
if self._get_value(opt) is None:
return True
return False
else:
value = list(value)
for val in value:
if val != None:
if val is not None:
return False
return True
def _test_mandatory(self, opt, value=None):
# mandatory options
mandatory = self.context._cfgimpl_settings.mandatory
if opt.is_mandatory() and mandatory:
def _test_mandatory(self, opt, value, force_properties=None):
setting = self.context.cfgimpl_get_settings()
if force_properties is None:
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 \
opt.is_empty_by_default():
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):
"""fills a multi option with default and calculated values
"""
value = self._get_value(opt)
if not isinstance(result, list):
_result = [result]
else:
_result = result
multitype = self._get_multitype(opt)
return Multi(_result, self.context, opt, multitype)
#multitype = self._get_multitype(opt)
return Multi(_result, self.context, opt) # , multitype)
def __getitem__(self, opt):
return self._getitem(opt)
def _getitem(self, opt, force_properties=None):
# options with callbacks
value = self._get_value(opt)
if opt.has_callback():
if (not opt.is_frozen() or \
not opt.is_forced_on_freeze()) and \
not opt.is_default_owner(self.context):
setting = self.context.cfgimpl_get_settings()
if (not setting.has_property('frozen', opt) or
(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)
try:
result = opt.getcallback_value(
self.context)
except NoValueReturned, err:
result = opt.getcallback_value(self.context)
except NoValueReturned:
pass
else:
if opt.is_multi():
@ -162,85 +141,94 @@ class Values(object):
# this result **shall not** be a list
if isinstance(result, list):
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
if value != None and not opt.validate(value,
self.context._cfgimpl_settings.validator):
if value is not None and \
not opt.validate(value, setting.has_property('validator')):
raise ConfigError('invalid calculated value returned'
' for option {0}'.format(opt._name))
' for option {0}'.format(opt._name))
# 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()
if opt.is_multi():
value = self.fill_multi(opt, value)
self._test_mandatory(opt, value)
self._test_mandatory(opt, value, force_properties)
return value
def __setitem__(self, opt, value):
if opt in self.masters:
masterlen = len(value)
for slave in self.masters[opt]:
value_slave = self._get_value(slave)
if len(value_slave) > masterlen:
raise MultiTypeError("invalid len for the slave: {0}"
" which has {1} as master".format(slave._name,
opt._name))
elif len(value_slave) < masterlen:
for num in range(0, masterlen - len(value_slave)):
value_slave.append(None, force=True)
if opt.is_multi():
if opt.multitype == multitypes.master:
masterlen = len(value)
for slave in self.opt.master_slaves:
value_slave = self._get_value(slave)
if len(value_slave) > masterlen:
raise MultiTypeError("invalid len for the slave: {0}"
" which has {1} as master".format(
slave._name, opt._name))
elif len(value_slave) < masterlen:
for num in range(0, masterlen - len(value_slave)):
value_slave.append(None, force=True)
elif opt in self.slaves:
if len(self._get_value(self.slaves[opt])) != len(value):
raise MultiTypeError("invalid len for the slave: {0}"
" which has {1} as master".format(opt._name,
self.slaves[opt]._name))
if opt.is_multi() and not isinstance(value, Multi):
value = Multi(value, self.context, opt, multitypes.default)
elif opt.multitype == multitypes.slave:
if len(self._get_value(self.opt.master_slaves)) != len(value):
raise MultiTypeError("invalid len for the slave: {0}"
" which has {1} as master".format(
opt._name, self.opt.master_slaves._name))
if not isinstance(value, Multi):
value = Multi(value, self.context, opt)
self.setitem(opt, value)
def setitem(self, opt, value):
self.set_previous_value(opt)
if type(value) == list:
raise MultiTypeError("the type of the value {0} which is multi shall "
"be Multi and not list".format(str(value)))
self.values[opt] = value
self.setowner(opt, self.context._cfgimpl_settings.getowner())
self._test_mandatory(opt, value)
self.values[opt] = (self.context.cfgimpl_get_settings().getowner(), value)
def __contains__(self, opt):
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):
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
class Multi(list):
"""multi options values container
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 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
"""
self.settings = context._cfgimpl_settings
self.opt = opt
self.values = context._cfgimpl_values
self.multitype = multitype
self.context = context
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):
self._validate(value)
self.values[self.opt] = self
self.context.cfgimpl_get_values()[self.opt] = self
super(Multi, self).__setitem__(key, value)
def append(self, value, force=False):
@ -248,20 +236,21 @@ class Multi(list):
only if the option is a master
"""
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}"
" which is a slave".format(self.opt._name))
elif self.multitype == multitypes.master:
for slave in self.slaves:
self.values[slave].append(None, force=True)
" which is a slave".format(self.opt._name))
elif self.opt.multitype == multitypes.master:
for slave in self.opt.master_slaves:
self.context.cfgimpl_get_values()[slave].append(None, force=True)
self._validate(value)
self.values.setitem(self.opt, self)
self.context.cfgimpl_get_values().setitem(self.opt, self)
super(Multi, self).append(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} "
"for option {1}".format(str(value), self.opt._name))
"for option {1}".format(str(value),
self.opt._name))
def pop(self, key, force=False):
"""the list value can be updated (poped)
@ -271,11 +260,11 @@ class Multi(list):
:return: the requested element
"""
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}"
" which is a slave".format(self.opt._name))
elif self.multitype == multitypes.master:
for slave in self.slaves:
self.values[slave].pop(key, force=True)
self.values.setitem(self.opt, self)
" which is a slave".format(self.opt._name))
elif self.opt.multitype == multitypes.master:
for slave in self.opt.master_slaves:
self.context.cfgimpl_get_values()[slave].pop(key, force=True)
self.context.cfgimpl_get_values().setitem(self.opt, self)
return super(Multi, self).pop(key)