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) g1 = IntOption('g1', '', 1)
d1 = OptionDescription('od1', '', [g1]) d1 = OptionDescription('od1', '', [g1])
#in different OptionDescription #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(): 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')] assert conf.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
conf.read_write() conf.read_write()
raises(AttributeError, "assert conf.find(byname='prop')") 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 # combinaison of filters
assert conf.find(bytype=BoolOption, byname='dummy') == [conf.unwrap_from_path('gc.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_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') 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, "assert conf.gc.find_first(byname='bool', byvalue=True)")
raises(AttributeError, "conf.gc.find(byname='wantref').first()") 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() conf.read_only()
assert conf.gc.find(byname='prop') == [conf.unwrap_from_path('gc.prop')] assert conf.gc.find(byname='prop') == [conf.unwrap_from_path('gc.prop')]
# not OptionDescription # not OptionDescription

View file

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

View file

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

View file

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

View file

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

View file

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