tests pass now with dictionary and sqlalchemy storage
This commit is contained in:
parent
0aeb64731b
commit
194c82faad
@ -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():
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
#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_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
|
||||
# #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():
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user