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
2 changed files with 119 additions and 6 deletions
|
@ -37,6 +37,84 @@ def test_slots_option():
|
|||
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():
|
||||
od1 = OptionDescription('a', '', [])
|
||||
od2 = OptionDescription('a', '', [od1])
|
||||
|
|
|
@ -91,7 +91,37 @@ class BaseInformation(object):
|
|||
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.
|
||||
|
||||
|
@ -450,10 +480,9 @@ else:
|
|||
raise ValueError(_('value must be an unicode'))
|
||||
|
||||
|
||||
class SymLinkOption(object):
|
||||
class SymLinkOption(_ReadOnlyOption):
|
||||
__slots__ = ('_name', '_opt')
|
||||
_opt_type = 'symlink'
|
||||
_consistencies = None
|
||||
|
||||
def __init__(self, name, opt):
|
||||
self._name = name
|
||||
|
@ -462,9 +491,10 @@ class SymLinkOption(object):
|
|||
'must be an option '
|
||||
'for symlink {0}').format(name))
|
||||
self._opt = opt
|
||||
self._readonly = True
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in ('_name', '_opt', '_consistencies'):
|
||||
if name in ('_name', '_opt', '_opt_type', '_readonly'):
|
||||
return object.__getattr__(self, name)
|
||||
else:
|
||||
return getattr(self._opt, name)
|
||||
|
@ -684,13 +714,13 @@ class DomainnameOption(Option):
|
|||
raise ValueError(_('invalid domainname'))
|
||||
|
||||
|
||||
class OptionDescription(BaseInformation):
|
||||
class OptionDescription(_ReadOnlyOption):
|
||||
"""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',
|
||||
'_properties', '_children', '_consistencies',
|
||||
'_calc_properties', '__weakref__')
|
||||
'_calc_properties', '__weakref__', '_readonly', '_impl_informations')
|
||||
|
||||
def __init__(self, name, doc, children, requires=None, properties=None):
|
||||
"""
|
||||
|
@ -731,6 +761,8 @@ class OptionDescription(BaseInformation):
|
|||
return self.impl_get_information('doc')
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self.__slots__:
|
||||
return object.__getattribute__(self, name)
|
||||
try:
|
||||
return self._children[1][self._children[0].index(name)]
|
||||
except ValueError:
|
||||
|
@ -769,6 +801,7 @@ class OptionDescription(BaseInformation):
|
|||
_currpath=None,
|
||||
_consistencies=None):
|
||||
if _currpath is None and self._cache_paths is not None:
|
||||
# cache already set
|
||||
return
|
||||
if _currpath is None:
|
||||
save = True
|
||||
|
@ -787,6 +820,7 @@ class OptionDescription(BaseInformation):
|
|||
raise ConflictError(_('duplicate option: {0}').format(option))
|
||||
|
||||
cache_option.append(option)
|
||||
option._readonly = True
|
||||
cache_path.append(str('.'.join(_currpath + [attr])))
|
||||
if not isinstance(option, OptionDescription):
|
||||
if option._consistencies is not None:
|
||||
|
@ -807,6 +841,7 @@ class OptionDescription(BaseInformation):
|
|||
if save:
|
||||
self._cache_paths = (tuple(cache_option), tuple(cache_path))
|
||||
self._consistencies = _consistencies
|
||||
self._readonly = True
|
||||
|
||||
def impl_get_opt_by_path(self, path):
|
||||
try:
|
||||
|
|
Loading…
Reference in a new issue