attributes in Option are now read-only if option set in Config (_name is everytime read-only)
This commit is contained in:
parent
c01f14920d
commit
5893f8ad72
|
@ -37,6 +37,84 @@ def test_slots_option():
|
||||||
raises(AttributeError, "c.x = 1")
|
raises(AttributeError, "c.x = 1")
|
||||||
|
|
||||||
|
|
||||||
|
def test_slots_option_readonly():
|
||||||
|
a = ChoiceOption('a', '', ('a',))
|
||||||
|
b = BoolOption('b', '')
|
||||||
|
c = IntOption('c', '')
|
||||||
|
d = FloatOption('d', '')
|
||||||
|
e = StrOption('e', '')
|
||||||
|
g = UnicodeOption('g', '')
|
||||||
|
h = IPOption('h', '')
|
||||||
|
i = PortOption('i', '')
|
||||||
|
j = NetworkOption('j', '')
|
||||||
|
k = NetmaskOption('k', '')
|
||||||
|
l = DomainnameOption('l', '')
|
||||||
|
m = OptionDescription('m', '', [a, b, c, d, e, g, h, i, j, k, l])
|
||||||
|
a._requires = 'a'
|
||||||
|
b._requires = 'b'
|
||||||
|
c._requires = 'c'
|
||||||
|
d._requires = 'd'
|
||||||
|
e._requires = 'e'
|
||||||
|
g._requires = 'g'
|
||||||
|
h._requires = 'h'
|
||||||
|
i._requires = 'i'
|
||||||
|
j._requires = 'j'
|
||||||
|
k._requires = 'k'
|
||||||
|
l._requires = 'l'
|
||||||
|
m._requires = 'm'
|
||||||
|
Config(m)
|
||||||
|
raises(AttributeError, "a._requires = 'a'")
|
||||||
|
raises(AttributeError, "b._requires = 'b'")
|
||||||
|
raises(AttributeError, "c._requires = 'c'")
|
||||||
|
raises(AttributeError, "d._requires = 'd'")
|
||||||
|
raises(AttributeError, "e._requires = 'e'")
|
||||||
|
raises(AttributeError, "g._requires = 'g'")
|
||||||
|
raises(AttributeError, "h._requires = 'h'")
|
||||||
|
raises(AttributeError, "i._requires = 'i'")
|
||||||
|
raises(AttributeError, "j._requires = 'j'")
|
||||||
|
raises(AttributeError, "k._requires = 'k'")
|
||||||
|
raises(AttributeError, "l._requires = 'l'")
|
||||||
|
raises(AttributeError, "m._requires = 'm'")
|
||||||
|
|
||||||
|
|
||||||
|
def test_slots_option_readonly_name():
|
||||||
|
a = ChoiceOption('a', '', ('a',))
|
||||||
|
b = BoolOption('b', '')
|
||||||
|
c = IntOption('c', '')
|
||||||
|
d = FloatOption('d', '')
|
||||||
|
e = StrOption('e', '')
|
||||||
|
f = SymLinkOption('f', c)
|
||||||
|
g = UnicodeOption('g', '')
|
||||||
|
h = IPOption('h', '')
|
||||||
|
i = PortOption('i', '')
|
||||||
|
j = NetworkOption('j', '')
|
||||||
|
k = NetmaskOption('k', '')
|
||||||
|
l = DomainnameOption('l', '')
|
||||||
|
m = OptionDescription('m', '', [a, b, c, d, e, f, g, h, i, j, k, l])
|
||||||
|
raises(AttributeError, "a._name = 'a'")
|
||||||
|
raises(AttributeError, "b._name = 'b'")
|
||||||
|
raises(AttributeError, "c._name = 'c'")
|
||||||
|
raises(AttributeError, "d._name = 'd'")
|
||||||
|
raises(AttributeError, "e._name = 'e'")
|
||||||
|
raises(AttributeError, "f._name = 'f'")
|
||||||
|
raises(AttributeError, "g._name = 'g'")
|
||||||
|
raises(AttributeError, "h._name = 'h'")
|
||||||
|
raises(AttributeError, "i._name = 'i'")
|
||||||
|
raises(AttributeError, "j._name = 'j'")
|
||||||
|
raises(AttributeError, "k._name = 'k'")
|
||||||
|
raises(AttributeError, "l._name = 'l'")
|
||||||
|
raises(AttributeError, "m._name = 'm'")
|
||||||
|
|
||||||
|
|
||||||
|
def test_slots_description():
|
||||||
|
# __slots__ for OptionDescription must be complete
|
||||||
|
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():
|
def test_slots_config():
|
||||||
od1 = OptionDescription('a', '', [])
|
od1 = OptionDescription('a', '', [])
|
||||||
od2 = OptionDescription('a', '', [od1])
|
od2 = OptionDescription('a', '', [od1])
|
||||||
|
|
|
@ -91,7 +91,37 @@ class BaseInformation(object):
|
||||||
self.__class__.__name__))
|
self.__class__.__name__))
|
||||||
|
|
||||||
|
|
||||||
class Option(BaseInformation):
|
class _ReadOnlyOption(BaseInformation):
|
||||||
|
__slots__ = ('_readonly',)
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
is_readonly = False
|
||||||
|
# never change _name
|
||||||
|
if name == '_name':
|
||||||
|
try:
|
||||||
|
self._name
|
||||||
|
#so _name is already set
|
||||||
|
is_readonly = True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
if self._readonly is True:
|
||||||
|
if value is True:
|
||||||
|
# already readonly and try to re set readonly
|
||||||
|
# don't raise, just exit
|
||||||
|
return
|
||||||
|
is_readonly = True
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
if is_readonly:
|
||||||
|
raise AttributeError(_("'{0}' ({1}) object attribute '{2}' is"
|
||||||
|
" read-only").format(
|
||||||
|
self.__class__.__name__, self._name,
|
||||||
|
name))
|
||||||
|
object.__setattr__(self, name, value)
|
||||||
|
|
||||||
|
|
||||||
|
class Option(_ReadOnlyOption):
|
||||||
"""
|
"""
|
||||||
Abstract base class for configuration option's.
|
Abstract base class for configuration option's.
|
||||||
|
|
||||||
|
@ -450,10 +480,9 @@ else:
|
||||||
raise ValueError(_('value must be an unicode'))
|
raise ValueError(_('value must be an unicode'))
|
||||||
|
|
||||||
|
|
||||||
class SymLinkOption(object):
|
class SymLinkOption(_ReadOnlyOption):
|
||||||
__slots__ = ('_name', '_opt')
|
__slots__ = ('_name', '_opt')
|
||||||
_opt_type = 'symlink'
|
_opt_type = 'symlink'
|
||||||
_consistencies = None
|
|
||||||
|
|
||||||
def __init__(self, name, opt):
|
def __init__(self, name, opt):
|
||||||
self._name = name
|
self._name = name
|
||||||
|
@ -462,9 +491,10 @@ class SymLinkOption(object):
|
||||||
'must be an option '
|
'must be an option '
|
||||||
'for symlink {0}').format(name))
|
'for symlink {0}').format(name))
|
||||||
self._opt = opt
|
self._opt = opt
|
||||||
|
self._readonly = True
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
if name in ('_name', '_opt', '_consistencies'):
|
if name in ('_name', '_opt', '_opt_type', '_readonly'):
|
||||||
return object.__getattr__(self, name)
|
return object.__getattr__(self, name)
|
||||||
else:
|
else:
|
||||||
return getattr(self._opt, name)
|
return getattr(self._opt, name)
|
||||||
|
@ -684,13 +714,13 @@ class DomainnameOption(Option):
|
||||||
raise ValueError(_('invalid domainname'))
|
raise ValueError(_('invalid domainname'))
|
||||||
|
|
||||||
|
|
||||||
class OptionDescription(BaseInformation):
|
class OptionDescription(_ReadOnlyOption):
|
||||||
"""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__ = ('_name', '_requires', '_cache_paths', '_group_type',
|
||||||
'_properties', '_children', '_consistencies',
|
'_properties', '_children', '_consistencies',
|
||||||
'_calc_properties', '__weakref__')
|
'_calc_properties', '__weakref__', '_readonly', '_impl_informations')
|
||||||
|
|
||||||
def __init__(self, name, doc, children, requires=None, properties=None):
|
def __init__(self, name, doc, children, requires=None, properties=None):
|
||||||
"""
|
"""
|
||||||
|
@ -731,6 +761,8 @@ class OptionDescription(BaseInformation):
|
||||||
return self.impl_get_information('doc')
|
return self.impl_get_information('doc')
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
|
if name in self.__slots__:
|
||||||
|
return object.__getattribute__(self, name)
|
||||||
try:
|
try:
|
||||||
return self._children[1][self._children[0].index(name)]
|
return self._children[1][self._children[0].index(name)]
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -769,6 +801,7 @@ class OptionDescription(BaseInformation):
|
||||||
_currpath=None,
|
_currpath=None,
|
||||||
_consistencies=None):
|
_consistencies=None):
|
||||||
if _currpath is None and self._cache_paths is not None:
|
if _currpath is None and self._cache_paths is not None:
|
||||||
|
# cache already set
|
||||||
return
|
return
|
||||||
if _currpath is None:
|
if _currpath is None:
|
||||||
save = True
|
save = True
|
||||||
|
@ -787,6 +820,7 @@ class OptionDescription(BaseInformation):
|
||||||
raise ConflictError(_('duplicate option: {0}').format(option))
|
raise ConflictError(_('duplicate option: {0}').format(option))
|
||||||
|
|
||||||
cache_option.append(option)
|
cache_option.append(option)
|
||||||
|
option._readonly = True
|
||||||
cache_path.append(str('.'.join(_currpath + [attr])))
|
cache_path.append(str('.'.join(_currpath + [attr])))
|
||||||
if not isinstance(option, OptionDescription):
|
if not isinstance(option, OptionDescription):
|
||||||
if option._consistencies is not None:
|
if option._consistencies is not None:
|
||||||
|
@ -807,6 +841,7 @@ class OptionDescription(BaseInformation):
|
||||||
if save:
|
if save:
|
||||||
self._cache_paths = (tuple(cache_option), tuple(cache_path))
|
self._cache_paths = (tuple(cache_option), tuple(cache_path))
|
||||||
self._consistencies = _consistencies
|
self._consistencies = _consistencies
|
||||||
|
self._readonly = True
|
||||||
|
|
||||||
def impl_get_opt_by_path(self, path):
|
def impl_get_opt_by_path(self, path):
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue