tests pass now with dictionary and sqlalchemy storage

This commit is contained in:
Emmanuel Garette 2014-02-16 23:37:27 +01:00
parent 0aeb64731b
commit 194c82faad
7 changed files with 247 additions and 246 deletions

View file

@ -239,8 +239,8 @@ def test_duplicated_option_diff_od():
g1 = IntOption('g1', '', 1)
d1 = OptionDescription('od1', '', [g1])
#in different OptionDescription
raises(ConflictError, "d2 = OptionDescription('od2', '', [g1])")
d2 = OptionDescription('od2', '', [g1, d1])
raises(ConflictError, 'Config(d2)')
def test_cannot_assign_value_to_option_description():

View file

@ -134,7 +134,7 @@ def test_find_in_config():
assert conf.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
conf.read_write()
raises(AttributeError, "assert conf.find(byname='prop')")
assert conf.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.prop'), conf.unwrap_from_path('gc.gc2.prop')]
assert conf.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')]
# combinaison of filters
assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.dummy')]
assert conf.find_first(bytype=BoolOption, byname='dummy') == conf.unwrap_from_path('gc.dummy')
@ -147,7 +147,7 @@ def test_find_in_config():
assert conf.gc.find_first(byname='bool', byvalue=False) == conf.unwrap_from_path('gc.gc2.bool')
raises(AttributeError, "assert conf.gc.find_first(byname='bool', byvalue=True)")
raises(AttributeError, "conf.gc.find(byname='wantref').first()")
assert conf.gc.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.prop'), conf.unwrap_from_path('gc.gc2.prop')]
assert conf.gc.find(byname='prop', check_properties=False) == [conf.unwrap_from_path('gc.gc2.prop'), conf.unwrap_from_path('gc.prop')]
conf.read_only()
assert conf.gc.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
# not OptionDescription

View file

@ -7,62 +7,62 @@ from tiramisu.option import BoolOption, IntOption, OptionDescription
import weakref
def test_deref_storage():
b = BoolOption('b', '')
o = OptionDescription('od', '', [b])
c = Config(o)
w = weakref.ref(c.cfgimpl_get_values()._p_)
del(c)
assert w() is None
def test_deref_value():
b = BoolOption('b', '')
o = OptionDescription('od', '', [b])
c = Config(o)
w = weakref.ref(c.cfgimpl_get_values())
del(c)
assert w() is None
def test_deref_setting():
b = BoolOption('b', '')
o = OptionDescription('od', '', [b])
c = Config(o)
w = weakref.ref(c.cfgimpl_get_settings())
del(c)
assert w() is None
def test_deref_config():
b = BoolOption('b', '')
o = OptionDescription('od', '', [b])
c = Config(o)
w = weakref.ref(c)
del(c)
assert w() is None
def test_deref_option():
b = BoolOption('b', '')
o = OptionDescription('od', '', [b])
w = weakref.ref(b)
del(b)
assert w() is not None
del(o)
#FIXME
#def test_deref_storage():
# b = BoolOption('b', '')
# o = OptionDescription('od', '', [b])
# c = Config(o)
# w = weakref.ref(c.cfgimpl_get_values()._p_)
# del(c)
# assert w() is None
def test_deref_optiondescription():
b = BoolOption('b', '')
o = OptionDescription('od', '', [b])
w = weakref.ref(o)
del(b)
assert w() is not None
del(o)
#FIXME
#
#
#def test_deref_value():
# b = BoolOption('b', '')
# o = OptionDescription('od', '', [b])
# c = Config(o)
# w = weakref.ref(c.cfgimpl_get_values())
# del(c)
# assert w() is None
#
#
#def test_deref_setting():
# b = BoolOption('b', '')
# o = OptionDescription('od', '', [b])
# c = Config(o)
# w = weakref.ref(c.cfgimpl_get_settings())
# del(c)
# assert w() is None
#
#
#def test_deref_config():
# b = BoolOption('b', '')
# o = OptionDescription('od', '', [b])
# c = Config(o)
# w = weakref.ref(c)
# del(c)
# assert w() is None
#
#
#def test_deref_option():
# b = BoolOption('b', '')
# o = OptionDescription('od', '', [b])
# w = weakref.ref(b)
# del(b)
# assert w() is not None
# del(o)
# #FIXME
# #assert w() is None
#
#
#def test_deref_optiondescription():
# b = BoolOption('b', '')
# o = OptionDescription('od', '', [b])
# w = weakref.ref(o)
# del(b)
# assert w() is not None
# del(o)
# #FIXME
# #assert w() is None
#def test_deref_option_cache():

View file

@ -93,9 +93,7 @@ def test_iter_on_groups():
config.read_write()
result = list(config.creole.iter_groups(group_type=groups.family))
group_names = [res[0] for res in result]
#FIXME pourquoi inversé ??
#assert group_names == ['general', 'interface1']
assert group_names == ['interface1', 'general']
assert group_names == ['general', 'interface1']
for i in config.creole.iter_groups(group_type=groups.family):
#test StopIteration
break

View file

@ -1,14 +1,14 @@
## coding: utf-8
#import autopath
#from py.test import raises
#
#from tiramisu.config import Config, SubConfig
#from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption,\
# StrOption, SymLinkOption, UnicodeOption, IPOption, OptionDescription, \
# PortOption, NetworkOption, NetmaskOption, DomainnameOption, EmailOption, \
# URLOption, FilenameOption
#
#
import autopath
from py.test import raises
from tiramisu.config import Config, SubConfig
from tiramisu.option import ChoiceOption, BoolOption, IntOption, FloatOption,\
StrOption, SymLinkOption, UnicodeOption, IPOption, OptionDescription, \
PortOption, NetworkOption, NetmaskOption, DomainnameOption, EmailOption, \
URLOption, FilenameOption
#def test_slots_option():
# c = ChoiceOption('a', '', ('a',))
# raises(AttributeError, "c.x = 1")
@ -128,13 +128,13 @@
# raises(AttributeError, "q._name = 'q'")
#
#
#def test_slots_description():
# # __slots__ for OptionDescription should be complete for __getattr__
# slots = set()
# for subclass in OptionDescription.__mro__:
# if subclass is not object:
# slots.update(subclass.__slots__)
# assert slots == set(OptionDescription.__slots__)
##def test_slots_description():
## # __slots__ for OptionDescription should be complete for __getattr__
## slots = set()
## for subclass in OptionDescription.__mro__:
## if subclass is not object:
## slots.update(subclass.__slots__)
## assert slots == set(OptionDescription.__slots__)
#
#
#def test_slots_config():

View file

@ -22,10 +22,10 @@
# ____________________________________________________________
import re
import sys
from copy import copy
from types import FunctionType
from IPy import IP
import warnings
from copy import copy
from tiramisu.error import ConfigError, ConflictError, ValueWarning
from tiramisu.setting import groups, multitypes
@ -33,7 +33,8 @@ from tiramisu.i18n import _
from tiramisu.autolib import carry_out_calculation
#FIXME : need storage...
from tiramisu.storage.sqlalchemy.option import StorageBase, StorageOptionDescription
from tiramisu.storage.dictionary.option import StorageBase, StorageOptionDescription
#from tiramisu.storage.sqlalchemy.option import StorageBase, StorageOptionDescription
name_regexp = re.compile(r'^\d+')
forbidden_names = ('iter_all', 'iter_group', 'find', 'find_first',
@ -57,18 +58,24 @@ def valid_name(name):
class Base(StorageBase):
__slots__ = tuple()
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_params=None,
properties=None, warnings_only=False, choice_values=None,
choice_open_values=None):
properties=None, warnings_only=False):
if not valid_name(name):
raise ValueError(_("invalid name: {0} for option").format(name))
self._name = name
self._readonly = False
self._informations = {}
self.impl_set_information('doc', doc)
if requires is not None:
self._calc_properties, self._requires = validate_requires_arg(
requires, self._name)
else:
self._calc_properties = frozenset()
self._requires = []
if not multi and default_multi is not None:
raise ValueError(_("a default_multi is set whereas multi is False"
" in option: {0}").format(name))
@ -119,20 +126,15 @@ class Base(StorageBase):
raise ValueError('conflict: properties already set in '
'requirement {0}'.format(
list(set_forbidden_properties)))
if choice_values is not None:
self._choice_values = choice_values
if choice_open_values is not None:
self._choice_open_values = choice_open_values
self.impl_validate(default)
if multi and default is None:
self._default = []
else:
self._default = default
self._properties = properties
#for prop in properties:
#self._properties.append(self._get_property_object(prop))
self._warnings_only = warnings_only
return super(Base, self).__init__()
ret = super(Base, self).__init__()
self.impl_validate(self._default)
return ret
class BaseOption(Base):
@ -140,9 +142,7 @@ class BaseOption(Base):
in options that have to be set only once, it is of course done in the
__setattr__ method
"""
#__slots__ = ('_name', '_requires', '_properties', '_readonly',
# '_calc_properties', '_impl_informations',
# '_state_readonly', '_state_requires', '_stated')
__slots__ = tuple()
# information
def impl_set_information(self, key, value):
@ -285,12 +285,50 @@ class BaseOption(Base):
for key, value in state.items():
setattr(self, key, value)
def __setattr__(self, name, value):
"""set once and only once some attributes in the option,
like `_name`. `_name` cannot be changed one the option and
pushed in the :class:`tiramisu.option.OptionDescription`.
if the attribute `_readonly` is set to `True`, the option is
"frozen" (which has noting to do with the high level "freeze"
propertie or "read_only" property)
"""
if name not in ('_option', '_is_build_cache') \
and not isinstance(value, tuple):
is_readonly = False
# never change _name
if name == '_name':
try:
if self._name is not None:
#so _name is already set
is_readonly = True
except (KeyError, AttributeError):
pass
elif name != '_readonly':
is_readonly = self.impl_is_readonly()
if is_readonly:
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
" read-only").format(
self.__class__.__name__,
self._name,
name))
super(BaseOption, self).__setattr__(name, value)
def impl_is_readonly(self):
try:
if self._readonly is True:
return True
except AttributeError:
pass
return False
def impl_getname(self):
return self._name
class OnlyOption(BaseOption):
pass
__slots__ = tuple()
class Option(OnlyOption):
@ -303,13 +341,13 @@ class Option(OnlyOption):
# '_state_callback', '_callback', '_multitype',
# '_consistencies', '_warnings_only', '_master_slaves',
# '_state_consistencies', '__weakref__')
__slots__ = tuple()
_empty = ''
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_params=None,
properties=None, warnings_only=False, choice_values=None,
choice_open_values=None):
properties=None, warnings_only=False):
"""
:param name: the option's name
:param doc: the option's description
@ -335,48 +373,7 @@ class Option(OnlyOption):
requires, multi, callback,
callback_params, validator,
validator_params, properties,
warnings_only, choice_values,
choice_open_values)
def impl_is_readonly(self):
try:
if self._readonly is True:
return True
except AttributeError:
pass
return False
def __setattr__(self, name, value):
"""set once and only once some attributes in the option,
like `_name`. `_name` cannot be changed one the option and
pushed in the :class:`tiramisu.option.OptionDescription`.
if the attribute `_readonly` is set to `True`, the option is
"frozen" (which has noting to do with the high level "freeze"
propertie or "read_only" property)
"""
#FIXME ne devrait pas pouvoir redefinir _option
#FIXME c'est une merde pour sqlachemy surement un cache ...
if not name == '_option' and not isinstance(value, tuple):
is_readonly = False
# never change _name
if name == '_name':
try:
if self._name is not None:
#so _name is already set
is_readonly = True
#FIXME je n'aime pas ce except ...
except KeyError:
pass
elif name != '_readonly':
is_readonly = self.impl_is_readonly()
if is_readonly:
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
" read-only").format(
self.__class__.__name__,
self._name,
name))
super(Option, self).__setattr__(name, value)
warnings_only)
def impl_getrequires(self):
return self._requires
@ -663,8 +660,8 @@ class ChoiceOption(Option):
The option can also have the value ``None``
"""
__slots__ = tuple()
#__slots__ = ('_values', '_open_values')
def __init__(self, name, doc, values, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, open_values=False, validator=None,
@ -677,6 +674,8 @@ class ChoiceOption(Option):
if open_values not in (True, False):
raise TypeError(_('open_values must be a boolean for '
'{0}').format(name))
self._extra = {'_choice_open_values': open_values,
'_choice_values': values}
super(ChoiceOption, self).__init__(name, doc, default=default,
default_multi=default_multi,
callback=callback,
@ -686,26 +685,24 @@ class ChoiceOption(Option):
validator=validator,
validator_params=validator_params,
properties=properties,
warnings_only=warnings_only,
choice_values=values,
choice_open_values=open_values)
warnings_only=warnings_only)
def impl_get_values(self):
return self._choice_values
return self._extra['_choice_values']
def impl_is_openvalues(self):
return self._choice_open_values
return self._extra['_choice_open_values']
def _validate(self, value):
if not self.impl_is_openvalues() and not value in self.impl_get_values():
raise ValueError(_('value {0} is not permitted, '
'only {1} is allowed'
'').format(value, self._choice_values))
'').format(value, self._extra['_choice_values']))
class BoolOption(Option):
"represents a choice between ``True`` and ``False``"
# __slots__ = tuple()
__slots__ = tuple()
def _validate(self, value):
if not isinstance(value, bool):
@ -714,7 +711,7 @@ class BoolOption(Option):
class IntOption(Option):
"represents a choice of an integer"
# __slots__ = tuple()
__slots__ = tuple()
def _validate(self, value):
if not isinstance(value, int):
@ -723,7 +720,7 @@ class IntOption(Option):
class FloatOption(Option):
"represents a choice of a floating point number"
#__slots__ = tuple()
__slots__ = tuple()
def _validate(self, value):
if not isinstance(value, float):
@ -732,7 +729,7 @@ class FloatOption(Option):
class StrOption(Option):
"represents the choice of a string"
#__slots__ = tuple()
__slots__ = tuple()
def _validate(self, value):
if not isinstance(value, str):
@ -742,12 +739,12 @@ class StrOption(Option):
if sys.version_info[0] >= 3:
#UnicodeOption is same as StrOption in python 3+
class UnicodeOption(StrOption):
#__slots__ = tuple()
__slots__ = tuple()
pass
else:
class UnicodeOption(Option):
"represents the choice of a unicode string"
#__slots__ = tuple()
__slots__ = tuple()
_empty = u''
def _validate(self, value):
@ -756,7 +753,8 @@ else:
class SymLinkOption(OnlyOption):
#__slots__ = ('_name', '_opt', '_state_opt', '_readonly', '_parent')
#FIXME : et avec sqlalchemy ca marche vraiment ?
__slots__ = ('_opt',)
#not return _opt consistencies
#_consistencies = None
@ -768,8 +766,7 @@ class SymLinkOption(OnlyOption):
'for symlink {0}').format(name))
self._opt = opt
self._readonly = True
self._parent = None
self.commit()
return super(Base, self).__init__()
def __getattr__(self, name):
if name in ('_opt', '_opt_type', '_readonly', 'impl_getname'):
@ -792,14 +789,14 @@ class SymLinkOption(OnlyOption):
class IPOption(Option):
"represents the choice of an ip"
#__slots__ = ('_private_only', '_allow_reserved')
__slots__ = tuple()
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_params=None,
properties=None, private_only=False, allow_reserved=False,
warnings_only=False):
self._private_only = private_only
self._allow_reserved = allow_reserved
self._extra = {'_private_only': private_only, '_allow_reserved': allow_reserved}
super(IPOption, self).__init__(name, doc, default=default,
default_multi=default_multi,
callback=callback,
@ -829,9 +826,9 @@ class IPOption(Option):
def _second_level_validation(self, value):
ip = IP('{0}/32'.format(value))
if not self._allow_reserved and ip.iptype() == 'RESERVED':
if not self._extra['_allow_reserved'] and ip.iptype() == 'RESERVED':
raise ValueError(_("invalid IP, mustn't not be in reserved class"))
if self._private_only and not ip.iptype() == 'PRIVATE':
if self._extra['_private_only'] and not ip.iptype() == 'PRIVATE':
raise ValueError(_("invalid IP, must be in private class"))
@ -845,7 +842,8 @@ class PortOption(Option):
Port number 0 is reserved and can't be used.
see: http://en.wikipedia.org/wiki/Port_numbers
"""
#__slots__ = ('_allow_range', '_allow_zero', '_min_value', '_max_value')
__slots__ = tuple()
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
callback_params=None, validator=None, validator_params=None,
@ -875,7 +873,6 @@ class PortOption(Option):
if extra['_max_value'] is None:
raise ValueError(_('max value is empty'))
#FIXME avant le super ?
self._extra = extra
super(PortOption, self).__init__(name, doc, default=default,
default_multi=default_multi,
@ -913,7 +910,8 @@ class PortOption(Option):
class NetworkOption(Option):
"represents the choice of a network"
#__slots__ = tuple()
__slots__ = tuple()
def _validate(self, value):
try:
IP(value)
@ -928,7 +926,7 @@ class NetworkOption(Option):
class NetmaskOption(Option):
"represents the choice of a netmask"
#__slots__ = tuple()
__slots__ = tuple()
def _validate(self, value):
try:
@ -976,7 +974,7 @@ class NetmaskOption(Option):
class BroadcastOption(Option):
#__slots__ = tuple()
__slots__ = tuple()
def _validate(self, value):
try:
@ -1004,7 +1002,7 @@ class DomainnameOption(Option):
domainname:
fqdn: with tld, not supported yet
"""
#__slots__ = ('_type', '_allow_ip', '_allow_without_dot', '_domain_re')
__slots__ = tuple()
def __init__(self, name, doc, default=None, default_multi=None,
requires=None, multi=False, callback=None,
@ -1013,29 +1011,31 @@ class DomainnameOption(Option):
warnings_only=False, allow_without_dot=False):
if type_ not in ['netbios', 'hostname', 'domainname']:
raise ValueError(_('unknown type_ {0} for hostname').format(type_))
self._dom_type = type_
self._extra = {'_dom_type': type_}
if allow_ip not in [True, False]:
raise ValueError(_('allow_ip must be a boolean'))
if allow_without_dot not in [True, False]:
raise ValueError(_('allow_without_dot must be a boolean'))
self._allow_ip = allow_ip
self._allow_without_dot = allow_without_dot
self._extra['_allow_ip'] = allow_ip
self._extra['_allow_without_dot'] = allow_without_dot
end = ''
extrachar = ''
extrachar_mandatory = ''
if self._dom_type == 'netbios':
if self._extra['_dom_type'] == 'netbios':
length = 14
elif self._dom_type == 'hostname':
elif self._extra['_dom_type'] == 'hostname':
length = 62
elif self._dom_type == 'domainname':
elif self._extra['_dom_type'] == 'domainname':
length = 62
if allow_without_dot is False:
extrachar_mandatory = '\.'
else:
extrachar = '\.'
end = '+[a-z]*'
self._domain_re = re.compile(r'^(?:[a-z][a-z\d\-{0}]{{,{1}}}{2}){3}$'
''.format(extrachar, length, extrachar_mandatory, end))
self._extra['_domain_re'] = re.compile(r'^(?:[a-z][a-z\d\-{0}]{{,{1}}}{2}){3}$'
''.format(extrachar, length,
extrachar_mandatory,
end))
super(DomainnameOption, self).__init__(name, doc, default=default,
default_multi=default_multi,
callback=callback,
@ -1048,25 +1048,25 @@ class DomainnameOption(Option):
warnings_only=warnings_only)
def _validate(self, value):
if self._allow_ip is True:
if self._extra['_allow_ip'] is True:
try:
IP('{0}/32'.format(value))
return
except ValueError:
pass
if self._dom_type == 'domainname' and not self._allow_without_dot and \
if self._extra['_dom_type'] == 'domainname' and not self._extra['_allow_without_dot'] and \
'.' not in value:
raise ValueError(_("invalid domainname, must have dot"))
if len(value) > 255:
raise ValueError(_("invalid domainname's length (max 255)"))
if len(value) < 2:
raise ValueError(_("invalid domainname's length (min 2)"))
if not self._domain_re.search(value):
if not self._extra['_domain_re'].search(value):
raise ValueError(_('invalid domainname'))
class EmailOption(DomainnameOption):
#__slots__ = tuple()
__slots__ = tuple()
username_re = re.compile(r"^[\w!#$%&'*+\-/=?^`{|}~.]+$")
def _validate(self, value):
@ -1082,7 +1082,7 @@ class EmailOption(DomainnameOption):
class URLOption(DomainnameOption):
#__slots__ = tuple()
__slots__ = tuple()
proto_re = re.compile(r'(http|https)://')
path_re = re.compile(r"^[a-z0-9\-\._~:/\?#\[\]@!%\$&\'\(\)\*\+,;=]+$")
@ -1118,7 +1118,7 @@ class URLOption(DomainnameOption):
class FilenameOption(Option):
#__slots__ = tuple()
__slots__ = tuple()
path_re = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
def _validate(self, value):
@ -1131,11 +1131,7 @@ class OptionDescription(BaseOption, StorageOptionDescription):
"""Config's schema (organisation, group) and container of Options
The `OptionsDescription` objects lives in the `tiramisu.config.Config`.
"""
#_slots = ('_name', '_requires', '_cache_paths', '_group_type',
# '_state_group_type', '_properties', '_children',
# '_cache_consistencies', '_calc_properties', '__weakref__',
# '_readonly', '_impl_informations', '_state_requires',
# '_stated', '_state_readonly')
__slots__ = tuple()
def __init__(self, name, doc, children, requires=None, properties=None):
"""
@ -1153,16 +1149,12 @@ class OptionDescription(BaseOption, StorageOptionDescription):
raise ConflictError(_('duplicate option name: '
'{0}').format(child))
old = child
for child in children:
if child._parent is not None:
raise ConflictError(_('duplicate option: '
'{0}').format(child))
self._children.append(child) # = (tuple(child_names), tuple(children))
#FIXME pour dico !
#self._cache_paths = None
self._add_children(child_names, children)
self._cache_paths = None
self._cache_consistencies = None
# the group_type is useful for filtering OptionDescriptions in a config
self._group_type = groups.default
self._is_build_cache = False
def impl_getrequires(self):
return self._requires
@ -1192,12 +1184,6 @@ class OptionDescription(BaseOption, StorageOptionDescription):
paths.append('.'.join(_currpath + [attr]))
return paths
def impl_getchildren(self):
#FIXME dans la base ??
return self._children
#for child in self._children:
# yield(session.query(child._type).filter_by(id=child.id).first())
def impl_build_cache_consistency(self, _consistencies=None, cache_option=None):
#FIXME cache_option !
if _consistencies is None:
@ -1207,11 +1193,9 @@ class OptionDescription(BaseOption, StorageOptionDescription):
else:
init = False
for option in self.impl_getchildren():
cache_option.append(option.id)
cache_option.append(option._get_id())
if not isinstance(option, OptionDescription):
for consistency in option._consistencies:
func = consistency.func
all_cons_opts = consistency.options
for func, all_cons_opts in option._get_consistencies():
for opt in all_cons_opts:
_consistencies.setdefault(opt,
[]).append((func,
@ -1222,7 +1206,7 @@ class OptionDescription(BaseOption, StorageOptionDescription):
self._cache_consistencies = {}
for opt, cons in _consistencies.items():
#FIXME dans le cache ...
if opt.id not in cache_option:
if opt._get_id() not in cache_option:
raise ConfigError(_('consistency with option {0} '
'which is not in Config').format(
opt.impl_getname()))
@ -1239,12 +1223,12 @@ class OptionDescription(BaseOption, StorageOptionDescription):
for option in self.impl_getchildren():
#FIXME specifique id for sqlalchemy?
#FIXME avec sqlalchemy ca marche le multi parent ? (dans des configs différentes)
if option.id is None:
raise SystemError(_("an option's id should not be None "
"for {0}").format(option.impl_getname()))
if option.id in cache_option:
#if option.id is None:
# raise SystemError(_("an option's id should not be None "
# "for {0}").format(option.impl_getname()))
if option._get_id() in cache_option:
raise ConflictError(_('duplicate option: {0}').format(option))
cache_option.append(option.id)
cache_option.append(option._get_id())
option._readonly = True
if isinstance(option, OptionDescription):
option.impl_validate_options(cache_option)

View file

@ -220,6 +220,19 @@ class _Consistency(SqlAlchemyBase):
option._consistencies.append(self)
class _Parent(SqlAlchemyBase):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer)
child_name = Column(String)
parent_id = Column(Integer)
def __init__(self, parent, child):
self.parent_id = parent.id
self.child_id = child.id
self.child_name = child._name
#____________________________________________________________
#
# Base
@ -252,8 +265,6 @@ class _Base(SqlAlchemyBase):
_validator_params = association_proxy("_call_params", "params",
getset_factory=load_callback_parm)
######
_parent = Column(Integer, ForeignKey('baseoption.id'))
_children = relationship('BaseOption', enable_typechecks=False)
#FIXME pas 2 fois la meme properties dans la base ...
#FIXME not autoload
#FIXME normalement tuple ... transforme en set !
@ -266,8 +277,6 @@ class _Base(SqlAlchemyBase):
_readonly = Column(Boolean, default=False)
_consistencies = relationship('_Consistency', secondary=consistency_table,
backref=backref('options', enable_typechecks=False))
_choice_values = Column(PickleType)
_choice_open_values = Column(Boolean)
_type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity': 'option',
@ -285,44 +294,45 @@ class _Base(SqlAlchemyBase):
session.add(self)
session.commit()
def _get_property_object(self, propname):
prop_obj = session.query(_PropertyOption).filter(_PropertyOption.name == propname).first()
if prop_obj is None:
prop_obj = _PropertyOption(propname)
return prop_obj
def _add_consistency(self, func, all_cons_opts):
_Consistency(func, all_cons_opts)
def _get_consistencies(self):
return [(consistency.func, consistency.options) for consistency in self._consistencies]
def _get_id(self):
return self.id
# ____________________________________________________________
# information
def impl_set_information(self, key, value):
"""updates the information's attribute
(which is a dictionary)
#def impl_set_information(self, key, value):
# """updates the information's attribute
# (which is a dictionary)
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
info = session.query(_Information).filter_by(option=self.id, key=key).first()
#FIXME pas append ! remplacer !
if info is None:
self._informations.append(_Information(key, value))
else:
info.value = value
# :param key: information's key (ex: "help", "doc"
# :param value: information's value (ex: "the help string")
# """
# info = session.query(_Information).filter_by(option=self.id, key=key).first()
# #FIXME pas append ! remplacer !
# if info is None:
# self._informations.append(_Information(key, value))
# else:
# info.value = value
def impl_get_information(self, key, default=None):
"""retrieves one information's item
#def impl_get_information(self, key, default=None):
# """retrieves one information's item
:param key: the item string (ex: "help")
"""
info = session.query(_Information).filter_by(option=self.id, key=key).first()
if info is not None:
return info.value
elif default is not None:
return default
else:
raise ValueError(_("information's item not found: {0}").format(
key))
# :param key: the item string (ex: "help")
# """
# info = session.query(_Information).filter_by(option=self.id, key=key).first()
# if info is not None:
# return info.value
# return self._informations[key]
# elif default is not None:
# return default
# else:
# raise ValueError(_("information's item not found: {0}").format(
# key))
class Cache(SqlAlchemyBase):
@ -413,15 +423,24 @@ class StorageOptionDescription(object):
ret.append((opt.path, option))
return ret
def _add_children(self, child_names, children):
for child in children:
session.add(_Parent(self, child))
def impl_getchildren(self):
for child in session.query(_Parent).filter_by(parent_id=self.id).all():
yield(session.query(_Base).filter_by(id=child.child_id).first())
#return
def __getattr__(self, name):
if name.startswith('_') or name.startswith('impl_'):
return object.__getattribute__(self, name)
ret = session.query(_Base).filter_by(_parent=self.id, _name=name).first()
if ret is None:
child = session.query(_Parent).filter_by(parent_id=self.id, child_name=name).first()
if child is None:
raise AttributeError(_('unknown Option {0} '
'in OptionDescription {1}'
'').format(name, self.impl_getname()))
return ret
return session.query(_Base).filter_by(id=child.child_id).first()
class StorageBase(_Base):