the value owner is a string now

This commit is contained in:
gwen 2012-11-15 10:55:14 +01:00
parent 5d23a4d921
commit eb7e393864
7 changed files with 91 additions and 183 deletions

View File

@ -40,7 +40,9 @@ def test_reset_value():
assert cfg.gc.dummy == False assert cfg.gc.dummy == False
cfg.gc.dummy = True cfg.gc.dummy = True
assert cfg.gc.dummy == True assert cfg.gc.dummy == True
cfg.gc.dummy = None # dummy = cfg.unwrap_from_path("gc.dummy")
# dummy.reset()
# cfg.gc.dummy = False
def test_base_config_and_groups(): def test_base_config_and_groups():
descr = make_description() descr = make_description()

View File

@ -124,19 +124,19 @@ def test_make_dict():
d2 = make_dict(config, flatten=True) d2 = make_dict(config, flatten=True)
assert d2 == {'a': True, 'int': 43} assert d2 == {'a': True, 'int': 43}
def test_delattr(): #def test_delattr():
"delattr, means suppression of an option in a config" # "delattr, means suppression of an option in a config"
descr = OptionDescription("opt", "", [ # descr = OptionDescription("opt", "", [
OptionDescription("s1", "", [ # OptionDescription("s1", "", [
BoolOption("a", "", default=False)]), # BoolOption("a", "", default=False)]),
IntOption("int", "", default=42)]) # IntOption("int", "", default=42)])
c = Config(descr) # c = Config(descr)
c.int = 45 # c.int = 45
assert c.int == 45 # assert c.int == 45
del c.int # del c.int
assert c.int == 42 # assert c.int == 42
c.int = 45 # c.int = 45
assert c.int == 45 # assert c.int == 45
def test_find_in_config(): def test_find_in_config():
"finds option in config" "finds option in config"

View File

@ -115,20 +115,20 @@ def test_choice_with_default():
config = Config(descr) config = Config(descr)
assert config.backend == "cli" assert config.backend == "cli"
def test_arbitrary_option(): #def test_arbitrary_option():
descr = OptionDescription("top", "", [ # descr = OptionDescription("top", "", [
ArbitraryOption("a", "no help", default=None) # ArbitraryOption("a", "no help", default=None)
]) # ])
config = Config(descr) # config = Config(descr)
config.a = [] # config.a = []
config.a.append(1) # config.a.append(1)
assert config.a == [1] # assert config.a == [1]
descr = OptionDescription("top", "", [ # descr = OptionDescription("top", "", [
ArbitraryOption("a", "no help", defaultfactory=list) # ArbitraryOption("a", "no help", defaultfactory=list)
]) # ])
c1 = Config(descr) # c1 = Config(descr)
c2 = Config(descr) # c2 = Config(descr)
c1.a.append(1) # c1.a.append(1)
assert c2.a == [] # assert c2.a == []
assert c1.a == [1] # assert c1.a == [1]

View File

@ -63,13 +63,13 @@ def test_reset_with_multi():
# config.string = [] # config.string = []
config.unwrap_from_path("string").reset(config) config.unwrap_from_path("string").reset(config)
assert config.string == ["string"] assert config.string == ["string"]
assert config._cfgimpl_value_owners['string'] == ['default'] assert config._cfgimpl_value_owners['string'] == 'default'
config.string = ["eggs", "spam", "foo"] config.string = ["eggs", "spam", "foo"]
assert config._cfgimpl_value_owners['string'] == ['user', 'user', 'user'] assert config._cfgimpl_value_owners['string'] == 'user'
config.string = [] config.string = []
config.unwrap_from_path("string").reset(config) config.unwrap_from_path("string").reset(config)
# assert config.string == ["string"] # assert config.string == ["string"]
assert config._cfgimpl_value_owners['string'] == ['default'] assert config._cfgimpl_value_owners['string'] == 'default'
raises(ConfigError, "config.string = None") raises(ConfigError, "config.string = None")
def test_default_with_multi(): def test_default_with_multi():
@ -111,10 +111,10 @@ def test_access_with_multi_default():
s = StrOption("string", "", default=["string"], multi=True) s = StrOption("string", "", default=["string"], multi=True)
descr = OptionDescription("options", "", [s]) descr = OptionDescription("options", "", [s])
config = Config(descr) config = Config(descr)
assert config._cfgimpl_value_owners["string"] == ['default'] assert config._cfgimpl_value_owners["string"] == 'default'
config.string = ["foo", "bar"] config.string = ["foo", "bar"]
assert config.string == ["foo", "bar"] assert config.string == ["foo", "bar"]
assert config._cfgimpl_value_owners["string"] == ['user', 'user'] assert config._cfgimpl_value_owners["string"] == 'user'
#def test_attribute_access_with_multi2(): #def test_attribute_access_with_multi2():
# s = StrOption("string", "", default="string", multi=True) # s = StrOption("string", "", default="string", multi=True)

View File

@ -25,7 +25,7 @@ from tiramisu.error import (PropertiesOptionError, ConfigError, NotFoundError,
AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound, AmbigousOptionError, ConflictConfigError, NoMatchingOptionFound,
MandatoryError, MethodCallError, NoValueReturned) MandatoryError, MethodCallError, NoValueReturned)
from tiramisu.option import (OptionDescription, Option, SymLinkOption, from tiramisu.option import (OptionDescription, Option, SymLinkOption,
group_types, Multi, apply_requires, Owner) group_types, Multi, apply_requires)
# ______________________________________________________________________ # ______________________________________________________________________
# generic owner. 'default' is the general config owner after init time # generic owner. 'default' is the general config owner after init time
@ -33,10 +33,11 @@ default_owner = 'user'
# ____________________________________________________________ # ____________________________________________________________
class Config(object): class Config(object):
"properties attribute: the name of a property enables this property" "main configuration management entry"
#properties attribute: the name of a property enables this property
_cfgimpl_properties = ['hidden', 'disabled'] _cfgimpl_properties = ['hidden', 'disabled']
_cfgimpl_permissive = [] _cfgimpl_permissive = []
"mandatory means: a mandatory option has to have a value that is not None" #mandatory means: a mandatory option has to have a value that is not None
_cfgimpl_mandatory = True _cfgimpl_mandatory = True
_cfgimpl_frozen = True _cfgimpl_frozen = True
_cfgimpl_owner = default_owner _cfgimpl_owner = default_owner
@ -87,13 +88,11 @@ class Config(object):
child=child) child=child)
self._cfgimpl_values[child._name] = childdef self._cfgimpl_values[child._name] = childdef
self._cfgimpl_previous_values[child._name] = list(childdef) self._cfgimpl_previous_values[child._name] = list(childdef)
self._cfgimpl_value_owners[child._name] = ['default' \
for i in range(len(child.getdefault() ))]
else: else:
childdef = child.getdefault() childdef = child.getdefault()
self._cfgimpl_values[child._name] = childdef self._cfgimpl_values[child._name] = childdef
self._cfgimpl_previous_values[child._name] = childdef self._cfgimpl_previous_values[child._name] = childdef
self._cfgimpl_value_owners[child._name] = 'default' self._cfgimpl_value_owners[child._name] = 'default'
elif isinstance(child, OptionDescription): elif isinstance(child, OptionDescription):
self._validate_duplicates(child._children) self._validate_duplicates(child._children)
self._cfgimpl_values[child._name] = Config(child, parent=self) self._cfgimpl_values[child._name] = Config(child, parent=self)
@ -115,24 +114,13 @@ class Config(object):
if child.is_multi(): if child.is_multi():
self._cfgimpl_values[child._name] = Multi( self._cfgimpl_values[child._name] = Multi(
copy(child.getdefault()), config=self, child=child) copy(child.getdefault()), config=self, child=child)
self._cfgimpl_value_owners[child._name] = ['default' \
for i in range(len(child.getdefault() ))]
else: else:
self._cfgimpl_values[child._name] = copy(child.getdefault()) self._cfgimpl_values[child._name] = copy(child.getdefault())
self._cfgimpl_value_owners[child._name] = 'default' self._cfgimpl_value_owners[child._name] = 'default'
elif isinstance(child, OptionDescription): elif isinstance(child, OptionDescription):
if child._name not in self._cfgimpl_values: if child._name not in self._cfgimpl_values:
self._cfgimpl_values[child._name] = Config(child, parent=self) self._cfgimpl_values[child._name] = Config(child, parent=self)
# def override(self, overrides):
# """
# overrides default values. This marks the overridden values as defaults.
# :param overrides: is a dictionary of path strings to values.
# """
# for name, value in overrides.iteritems():
# homeconfig, name = self._cfgimpl_get_home_by_path(name)
# homeconfig.setoption(name, value, 'default')
def cfgimpl_set_owner(self, owner): def cfgimpl_set_owner(self, owner):
":param owner: sets the default value for owner at the Config level" ":param owner: sets the default value for owner at the Config level"
self._cfgimpl_owner = owner self._cfgimpl_owner = owner
@ -254,8 +242,7 @@ class Config(object):
value = self._cfgimpl_values[name] value = self._cfgimpl_values[name]
if (not opt_or_descr.is_frozen() or \ if (not opt_or_descr.is_frozen() or \
not opt_or_descr.is_forced_on_freeze()) and \ not opt_or_descr.is_forced_on_freeze()) and \
not opt_or_descr.is_default_owner(self, all_default=False): not opt_or_descr.is_default_owner(self):
#not opt_or_descr.getowner(self) == 'default':
if opt_or_descr.is_multi(): if opt_or_descr.is_multi():
if None not in value: if None not in value:
return value return value
@ -268,42 +255,9 @@ class Config(object):
pass pass
else: else:
if opt_or_descr.is_multi(): if opt_or_descr.is_multi():
owners = copy(self._cfgimpl_value_owners[name])
self._cfgimpl_value_owners[name] = []
if not isinstance(result, list): if not isinstance(result, list):
# for example, [1, 2, 3, None] -> [1, 2, 3, result] result = [result]
_result = Multi([result], value.config, value.child) _result = Multi(result, value.config, value.child)
for cpt in range(len(value)):
val = value[cpt]
if len(owners) > cpt:
if owners[cpt] == 'default':
_result.append(result)
self._cfgimpl_value_owners[name][cpt] = 'default'
else:
_result.append(val)
else:
_result.append(val)
self._cfgimpl_value_owners[name][cpt] = 'default'
else:
# for example, [1, None, 2, None] + [a, b, c, d]
# = [1, b, 2, d]
_result = Multi([], value.config, value.child)
for cpt in range(max(len(value), len(result))):
if len(value) > cpt:
val = value[cpt]
else:
val = ''
if len(result) > cpt:
rval = result[cpt]
if len(owners) > cpt:
if owners[cpt] == 'default':
_result.append(rval)
self._cfgimpl_value_owners[name][cpt] = 'default'
else:
_result.append(val)
else:
_result.append(rval)
self._cfgimpl_value_owners[name][cpt] = 'default'
else: else:
# this result **shall not** be a list # this result **shall not** be a list
if isinstance(result, list): if isinstance(result, list):
@ -314,6 +268,7 @@ class Config(object):
raise ConfigError('invalid calculated value returned' raise ConfigError('invalid calculated value returned'
' for option {0}'.format(name)) ' for option {0}'.format(name))
self._cfgimpl_values[name] = _result self._cfgimpl_values[name] = _result
self._cfgimpl_value_owners[name] = 'default'
self._test_mandatory(name, opt_or_descr) self._test_mandatory(name, opt_or_descr)
# frozen and force default # frozen and force default
if not opt_or_descr.has_callback() and opt_or_descr.is_forced_on_freeze(): if not opt_or_descr.has_callback() and opt_or_descr.is_forced_on_freeze():
@ -347,16 +302,16 @@ class Config(object):
return getattr(homeconfig._cfgimpl_descr, path) return getattr(homeconfig._cfgimpl_descr, path)
return getattr(self._cfgimpl_descr, path) return getattr(self._cfgimpl_descr, path)
def __delattr__(self, name): #def __delattr__(self, name):
"if you use delattr you are responsible for all bad things happening" # "if you use delattr you are responsible for all bad things happening"
if name.startswith('_cfgimpl_'): # if name.startswith('_cfgimpl_'):
del self.__dict__[name] # del self.__dict__[name]
return # return
self._cfgimpl_value_owners[name] = 'default' # self._cfgimpl_value_owners[name] = 'default'
opt = getattr(self._cfgimpl_descr, name) # opt = getattr(self._cfgimpl_descr, name)
if isinstance(opt, OptionDescription): # if isinstance(opt, OptionDescription):
raise AttributeError("can't option subgroup") # raise AttributeError("can't option subgroup")
self._cfgimpl_values[name] = getattr(opt, 'default', None) # self._cfgimpl_values[name] = getattr(opt, 'default', None)
def setoption(self, name, value, who=None): def setoption(self, name, value, who=None):
"""effectively modifies the value of an Option() """effectively modifies the value of an Option()
@ -377,14 +332,8 @@ class Config(object):
else: else:
raise ConfigError("invalid value for option:" raise ConfigError("invalid value for option:"
" {0} that is set to multi".format(name)) " {0} that is set to multi".format(name))
newowner = [who for i in range(len(value))]
else:
newowner = who
child.setoption(self, value, who) child.setoption(self, value, who)
if child.is_multi() and value == [] and who != 'default': child.setowner(self, who)
child.setowner(self, Owner(who))
else:
child.setowner(self, newowner)
else: else:
homeconfig = self._cfgimpl_get_toplevel() homeconfig = self._cfgimpl_get_toplevel()
child.setoption(homeconfig, value, who) child.setoption(homeconfig, value, who)

View File

@ -47,15 +47,6 @@ group_types = ['default', 'family', 'group', 'master']
# ____________________________________________________________ # ____________________________________________________________
# multi types # multi types
class Owner(str):
"an owner just for a multi Option that have no value set"
# we need a string that cannot be iterable
def __iter__(self):
raise StopIteration
def __len__(self):
return 0
class Multi(list): class Multi(list):
"container that support items for the values of list (multi) options" "container that support items for the values of list (multi) options"
def __init__(self, lst, config, child): def __init__(self, lst, config, child):
@ -72,28 +63,21 @@ class Multi(list):
def setoption(self, value, key=None, who=None): def setoption(self, value, key=None, who=None):
if who is None: if who is None:
who = self.config._cfgimpl_owner who = self.config._cfgimpl_owner
if not self.child._validate(value): if value != None:
raise ConfigError("invalid value {0} " if not self.child._validate(value):
"for option {1}".format(str(value), self.child._name)) raise ConfigError("invalid value {0} "
"for option {1}".format(str(value), self.child._name))
oldvalue = list(self) oldvalue = list(self)
oldowner = self.child.getowner(self.config)
if isinstance(oldowner, Owner):
oldowner = []
if key is None: if key is None:
ret = super(Multi, self).append(value) ret = super(Multi, self).append(value)
oldvalue.append(None)
oldowner.append(who)
else: else:
ret = super(Multi, self).__setitem__(key, value) ret = super(Multi, self).__setitem__(key, value)
oldowner[key] = who self.child.setowner(self.config, who)
self.config._cfgimpl_previous_values[self.child._name] = oldvalue self.config._cfgimpl_previous_values[self.child._name] = oldvalue
self.child.setowner(self.config, oldowner)
return ret return ret
def pop(self, key): def pop(self, key):
oldowner = self.child.getowner(self.config) self.child.setowner(self.config, self.config._cfgimpl_owner)
oldowner.pop(key)
self.child.setowner(self.config, oldowner)
super(Multi, self).pop(key) super(Multi, self).pop(key)
# ____________________________________________________________ # ____________________________________________________________
# #
@ -219,10 +203,9 @@ class Option(HiddenBaseType, DisabledBaseType):
which is allowable here which is allowable here
""" """
name = self._name name = self._name
if self.is_multi(): if not type(owner) == str:
if not type(owner) == list and not isinstance(owner, Owner): raise ConfigError("invalid type for owner option: {0}".format(
raise ConfigError("invalid owner for multi " name))
"option: {0}".format(name))
config._cfgimpl_value_owners[name] = owner config._cfgimpl_value_owners[name] = owner
def getowner(self, config): def getowner(self, config):
@ -236,13 +219,14 @@ class Option(HiddenBaseType, DisabledBaseType):
if self.is_multi(): if self.is_multi():
if idx is not None: if idx is not None:
defval = self.getdefault() defval = self.getdefault()
value = getattr(config, self._name)
# if the default is ['a', 'b', 'c'] # if the default is ['a', 'b', 'c']
if len(defval) > idx: if len(defval) > idx:
# and idx = 4 -> there is actually no such value in the default
value.setoption(default_multi, idx, who='default')
else:
# and idx = 2 -> there is a value in the default # and idx = 2 -> there is a value in the default
value.setoption(defval[idx], idx, who='default') value.setoption(defval[idx], idx, who='default')
else:
# and idx = 4 -> there is actually no such value in the default
value.setoption(self.default_multi, idx, who='default')
else: else:
value = Multi(self.getdefault(), config, self) value = Multi(self.getdefault(), config, self)
config.setoption(self._name, value, 'default') config.setoption(self._name, value, 'default')
@ -250,34 +234,13 @@ class Option(HiddenBaseType, DisabledBaseType):
value = self.getdefault() value = self.getdefault()
config.setoption(self._name, value, 'default') config.setoption(self._name, value, 'default')
def is_default_owner(self, config, all_default=True, at_index=None): def is_default_owner(self, config):
""" """
:param config: *must* be only the **parent** config :param config: *must* be only the **parent** config
(not the toplevel config) (not the toplevel config)
:param all_default: only for multi options, if True and the owner list
has something else than "default" returns False, if False and the owner
list has at least one "default" owner, returns True
:param at_index: only for multi options, checks owner at specified
index
:return: boolean :return: boolean
""" """
if self.is_multi(): return self.getowner(config) == 'default'
owners = self.getowner(config)
if at_index:
return owners[at_index] == 'default'
for owner in owners:
if all_default and owner != 'default':
return False
if not all_default and owner == 'default':
return True
if all_default or owners == []:
return True
else:
return False
else:
if at_index:
raise ValueError('index specified for a not multi option')
return self.getowner(config) == 'default'
def setoption(self, config, value, who): def setoption(self, config, value, who):
"""changes the option's value with the value_owner's who """changes the option's value with the value_owner's who
@ -405,22 +368,17 @@ class NetmaskOption(Option):
# by now the validation is nothing but a string, use IPy instead # by now the validation is nothing but a string, use IPy instead
return isinstance(value, str) return isinstance(value, str)
class ArbitraryOption(Option): #class ArbitraryOption(Option):
def __init__(self, name, doc, default=None, defaultfactory=None, # def __init__(self, name, doc, default=None, defaultfactory=None,
requires=None, multi=False, mandatory=False): # requires=None, multi=False, mandatory=False):
super(ArbitraryOption, self).__init__(name, doc, requires=requires, # super(ArbitraryOption, self).__init__(name, doc, requires=requires,
multi=multi, mandatory=mandatory) # multi=multi, mandatory=mandatory)
self.defaultfactory = defaultfactory # self.defaultfactory = defaultfactory
if defaultfactory is not None: # if defaultfactory is not None:
assert default is None # assert default is None
def _validate(self, value): # def _validate(self, value):
return True # return True
def getdefault(self):
if self.defaultfactory is not None:
return self.defaultfactory()
return self.default
class OptionDescription(HiddenBaseType, DisabledBaseType): class OptionDescription(HiddenBaseType, DisabledBaseType):
"Config's schema (organisation) and container of Options" "Config's schema (organisation) and container of Options"

View File

@ -14,13 +14,13 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
# The original `Config` design model is unproudly borrowed from # The original `Config` design model is unproudly borrowed from
# the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/ # the rough gus of pypy: pypy: http://codespeak.net/svn/pypy/dist/pypy/config/
# the whole pypy projet is under MIT licence # the whole pypy projet is under MIT licence
from tiramisu.config import Config from tiramisu.config import Config
from tiramisu.option import (OptionDescription, Option, ChoiceOption, BoolOption, from tiramisu.option import (OptionDescription, Option, ChoiceOption, BoolOption,
FloatOption, StrOption, IntOption, IPOption, NetmaskOption, FloatOption, StrOption, IntOption, IPOption, NetmaskOption,
ArbitraryOption, group_types, apply_requires) group_types, apply_requires)
# ____________________________________________________________ # ____________________________________________________________
# reverse factory # reverse factory
@ -38,7 +38,7 @@ def reverse_from_paths(data):
"dummy -> Option('dummy')" "dummy -> Option('dummy')"
if isinstance(value, list): if isinstance(value, list):
return _build_map[type(value[0])](name, '', multi=True, default=value) return _build_map[type(value[0])](name, '', multi=True, default=value)
else: else:
return _build_map[type(value)](name, '', default=value) return _build_map[type(value)](name, '', default=value)
def build_options(data): def build_options(data):
@ -51,7 +51,7 @@ def reverse_from_paths(data):
"config.gc.dummy -> config.gc" "config.gc.dummy -> config.gc"
if "." in pathname: if "." in pathname:
return ".".join(pathname.split('.')[:-1]) return ".".join(pathname.split('.')[:-1])
# no parent except rootconfig, naturally returns None # no parent except rootconfig, naturally returns None
def subgroups(pathname): def subgroups(pathname):
"config.gc.dummy.bool -> [config.gc, config.gc.dummy]" "config.gc.dummy.bool -> [config.gc, config.gc.dummy]"
@ -60,13 +60,13 @@ def reverse_from_paths(data):
while group is not None: while group is not None:
parents.append(group) parents.append(group)
group = parent(group) group = parent(group)
return parents return parents
def build_option_descriptions(data): def build_option_descriptions(data):
all_groups = [] all_groups = []
for key in data.keys(): for key in data.keys():
for group in subgroups(key): for group in subgroups(key):
# so group is unique in the list # so group is unique in the list
if group not in all_groups: if group not in all_groups:
all_groups.append(group) all_groups.append(group)
for group in all_groups: for group in all_groups:
@ -126,4 +126,3 @@ class extend(type):
setattr(cls, key, value) setattr(cls, key, value)
# ____________________________________________________________ # ____________________________________________________________