811 lines
32 KiB
Python
811 lines
32 KiB
Python
# -*- coding: utf-8 -*-
|
|
"sets the options of the configuration objects Config object itself"
|
|
# Copyright (C) 2012-2018 Team tiramisu (see AUTHORS for all contributors)
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Lesser General Public License as published by the
|
|
# Free Software Foundation, either version 3 of the License, or (at your
|
|
# option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
# ____________________________________________________________
|
|
from time import time
|
|
from copy import copy
|
|
from logging import getLogger
|
|
import weakref
|
|
from .error import (RequirementError, PropertiesOptionError,
|
|
ConstError, ConfigError, display_list)
|
|
from .i18n import _
|
|
|
|
|
|
"Default encoding for display a Config if raise UnicodeEncodeError"
|
|
default_encoding = 'utf-8'
|
|
|
|
"""If cache and expire is enable, time before cache is expired.
|
|
This delay start first time value/setting is set in cache, even if
|
|
user access several time to value/setting
|
|
"""
|
|
expires_time = 5
|
|
"""List of default properties (you can add new one if needed).
|
|
|
|
For common properties and personalise properties, if a propery is set for
|
|
an Option and for the Config together, Setting raise a PropertiesOptionError
|
|
|
|
* Common properties:
|
|
|
|
hidden
|
|
option with this property can only get value in read only mode. This
|
|
option is not available in read write mode.
|
|
|
|
disabled
|
|
option with this property cannot be set/get
|
|
|
|
frozen
|
|
cannot set value for option with this properties if 'frozen' is set in
|
|
config
|
|
|
|
mandatory
|
|
should set value for option with this properties if 'mandatory' is set in
|
|
config
|
|
|
|
|
|
* Special property:
|
|
|
|
permissive
|
|
option with 'permissive' cannot raise PropertiesOptionError for properties
|
|
set in permissive
|
|
config with 'permissive', whole option in this config cannot raise
|
|
PropertiesOptionError for properties set in permissive
|
|
|
|
* Special Config properties:
|
|
|
|
cache
|
|
if set, enable cache settings and values
|
|
|
|
expire
|
|
if set, settings and values in cache expire after ``expires_time``
|
|
|
|
everything_frozen
|
|
whole option in config are frozen (even if option have not frozen
|
|
property)
|
|
|
|
empty
|
|
raise mandatory PropertiesOptionError if multi or master have empty value
|
|
|
|
validator
|
|
launch validator set by user in option (this property has no effect
|
|
for internal validator)
|
|
|
|
warnings
|
|
display warnings during validation
|
|
"""
|
|
default_properties = ('cache', 'validator', 'warnings')
|
|
|
|
"""Config can be in two defaut mode:
|
|
|
|
read_only
|
|
you can get all variables not disabled but you cannot set any variables
|
|
if a value has a callback without any value, callback is launch and value
|
|
of this variable can change
|
|
you cannot access to mandatory variable without values
|
|
|
|
read_write
|
|
you can get all variables not disabled and not hidden
|
|
you can set all variables not frozen
|
|
"""
|
|
ro_append = set(['frozen', 'disabled', 'validator', 'everything_frozen',
|
|
'mandatory', 'empty'])
|
|
ro_remove = set(['permissive', 'hidden'])
|
|
rw_append = set(['frozen', 'disabled', 'validator', 'hidden'])
|
|
rw_remove = set(['permissive', 'everything_frozen', 'mandatory', 'empty'])
|
|
|
|
|
|
FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value'])
|
|
FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze'])
|
|
|
|
|
|
log = getLogger('tiramisu')
|
|
#FIXME
|
|
#import logging
|
|
#logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
|
|
debug = False
|
|
static_set = frozenset()
|
|
|
|
|
|
class ConfigBag(object):
|
|
__slots__ = ('default',
|
|
'config',
|
|
'option',
|
|
'ori_option',
|
|
'properties',
|
|
'setting_properties',
|
|
'force_permissive',
|
|
'force_unrestraint',
|
|
'trusted_cached_properties',
|
|
'fromconsistency',
|
|
'_validator'
|
|
)
|
|
def __init__(self, config, **kwargs):
|
|
self.default = {'force_permissive': False,
|
|
'force_unrestraint': False,
|
|
'trusted_cached_properties': True,
|
|
}
|
|
self.config = config
|
|
self._validator = True
|
|
self.fromconsistency = []
|
|
for key, value in kwargs.items():
|
|
if value != self.default.get(key):
|
|
setattr(self, key, value)
|
|
|
|
def __getattr__(self, key):
|
|
if key == 'validate_properties':
|
|
return not self.force_unrestraint
|
|
if key == 'validate':
|
|
if self.setting_properties is not None:
|
|
return 'validator' in self.setting_properties
|
|
return self._validator
|
|
if key == 'setting_properties':
|
|
if self.force_unrestraint:
|
|
return None
|
|
self.setting_properties = self.config.cfgimpl_get_settings().get_context_properties()
|
|
return self.setting_properties
|
|
if key not in self.__slots__:
|
|
raise KeyError('unknown key {}'.format(key))
|
|
return self.default.get(key)
|
|
|
|
def __setattr__(self, key, value):
|
|
if key == 'validate':
|
|
if self.setting_properties is not None:
|
|
if value is False:
|
|
self.setting_properties = frozenset(set(self.setting_properties) - {'validator'})
|
|
else:
|
|
self.setting_properties = frozenset(set(self.setting_properties) | {'validator'})
|
|
else:
|
|
self._validator = value
|
|
else:
|
|
super().__setattr__(key, value)
|
|
|
|
def delete(self, key):
|
|
try:
|
|
return self.__delattr__(key)
|
|
except AttributeError:
|
|
pass
|
|
|
|
def copy(self, filters='all'):
|
|
kwargs = {}
|
|
for key in self.__slots__:
|
|
if filters == 'nooption' and (key.startswith('option') or \
|
|
key == 'properties'):
|
|
continue
|
|
if key == 'fromconsistency':
|
|
kwargs['fromconsistency'] = copy(self.fromconsistency)
|
|
elif key != 'default':
|
|
value = getattr(self, key)
|
|
if value != self.default.get(key):
|
|
kwargs[key] = value
|
|
return ConfigBag(**kwargs)
|
|
|
|
|
|
# ____________________________________________________________
|
|
class _NameSpace(object):
|
|
"""convenient class that emulates a module
|
|
and builds constants (that is, unique names)
|
|
when attribute is added, we cannot delete it
|
|
"""
|
|
|
|
def __setattr__(self,
|
|
name,
|
|
value):
|
|
if name in self.__dict__:
|
|
raise ConstError(_("can't rebind {0}").format(name))
|
|
self.__dict__[name] = value
|
|
|
|
def __delattr__(self,
|
|
name):
|
|
raise ConstError(_("can't unbind {0}").format(name))
|
|
|
|
|
|
class GroupModule(_NameSpace):
|
|
"emulates a module to manage unique group (OptionDescription) names"
|
|
class GroupType(str):
|
|
"""allowed normal group (OptionDescription) names
|
|
*normal* means : groups that are not master
|
|
"""
|
|
pass
|
|
|
|
class DefaultGroupType(GroupType):
|
|
"""groups that are default (typically 'default')"""
|
|
pass
|
|
|
|
class MasterGroupType(GroupType):
|
|
"""allowed normal group (OptionDescription) names
|
|
*master* means : groups that have the 'master' attribute set
|
|
"""
|
|
pass
|
|
|
|
|
|
class OwnerModule(_NameSpace):
|
|
"""emulates a module to manage unique owner names.
|
|
|
|
owners are living in `Config._cfgimpl_value_owners`
|
|
"""
|
|
class Owner(str):
|
|
"""allowed owner names
|
|
"""
|
|
pass
|
|
|
|
class DefaultOwner(Owner):
|
|
"""groups that are default (typically 'default')"""
|
|
pass
|
|
|
|
def addowner(self, name):
|
|
"""
|
|
:param name: the name of the new owner
|
|
"""
|
|
setattr(owners, name, owners.Owner(name))
|
|
|
|
|
|
# ____________________________________________________________
|
|
# populate groups
|
|
groups = GroupModule()
|
|
"""groups.default
|
|
default group set when creating a new optiondescription"""
|
|
groups.default = groups.DefaultGroupType('default')
|
|
|
|
"""groups.master
|
|
master group is a special optiondescription, all suboptions should be
|
|
multi option and all values should have same length, to find master's
|
|
option, the optiondescription's name should be same than de master's
|
|
option"""
|
|
groups.master = groups.MasterGroupType('master')
|
|
|
|
""" groups.family
|
|
example of group, no special behavior with this group's type"""
|
|
groups.family = groups.GroupType('family')
|
|
|
|
|
|
# ____________________________________________________________
|
|
# populate owners with default attributes
|
|
owners = OwnerModule()
|
|
"""default
|
|
is the config owner after init time"""
|
|
owners.default = owners.DefaultOwner('default')
|
|
"""user
|
|
is the generic is the generic owner"""
|
|
owners.user = owners.Owner('user')
|
|
"""forced
|
|
special owner when value is forced"""
|
|
owners.forced = owners.Owner('forced')
|
|
"""meta
|
|
special owner when value comes from metaconfig"""
|
|
owners.meta = owners.Owner('meta')
|
|
|
|
|
|
forbidden_owners = (owners.default, owners.forced, owners.meta)
|
|
|
|
|
|
# ____________________________________________________________
|
|
class Undefined(object):
|
|
def __str__(self):
|
|
return 'Undefined'
|
|
|
|
__repr__ = __str__
|
|
|
|
|
|
undefined = Undefined()
|
|
|
|
|
|
#____________________________________________________________
|
|
class Settings(object):
|
|
"``config.Config()``'s configuration options settings"
|
|
__slots__ = ('context', '_owner', '_p_', '_pp_', '__weakref__')
|
|
|
|
def __init__(self, context, properties, permissives):
|
|
"""
|
|
initializer
|
|
|
|
:param context: the root config
|
|
:param storage: the storage type
|
|
|
|
- dictionary -> in memory
|
|
- sqlite3 -> persistent
|
|
"""
|
|
# generic owner
|
|
self._owner = owners.user
|
|
self.context = weakref.ref(context)
|
|
self._p_ = properties
|
|
self._pp_ = permissives
|
|
|
|
def _getcontext(self):
|
|
"""context could be None, we need to test it
|
|
context is None only if all reference to `Config` object is deleted
|
|
(for example we delete a `Config` and we manipulate a reference to
|
|
old `SubConfig`, `Values`, `Multi` or `Settings`)
|
|
"""
|
|
context = self.context()
|
|
if context is None: # pragma: no cover
|
|
raise ConfigError(_('the context does not exist anymore'))
|
|
return context
|
|
|
|
#____________________________________________________________
|
|
# get properties and permissive methods
|
|
|
|
def get_context_properties(self):
|
|
ntime = int(time())
|
|
if self._p_.hascache(None,
|
|
None):
|
|
is_cached, props = self._p_.getcache(None,
|
|
ntime,
|
|
None)
|
|
else:
|
|
is_cached = False
|
|
if not is_cached or 'cache' not in props:
|
|
meta = self._getcontext().cfgimpl_get_meta()
|
|
if meta is None:
|
|
props = self._p_.getproperties(None,
|
|
default_properties)
|
|
else:
|
|
props = meta.cfgimpl_get_settings().get_context_properties()
|
|
if 'cache' in props:
|
|
if 'expire' in props:
|
|
ntime = ntime + expires_time
|
|
else:
|
|
ntime = None
|
|
self._p_.setcache(None, props, ntime, None)
|
|
return props
|
|
|
|
def getproperties(self,
|
|
path,
|
|
index,
|
|
config_bag,
|
|
apply_requires=True):
|
|
"""
|
|
"""
|
|
opt = config_bag.option
|
|
if opt.impl_is_symlinkoption():
|
|
opt = opt.impl_getopt()
|
|
path = opt.impl_getpath(self._getcontext())
|
|
is_cached = False
|
|
|
|
if apply_requires and config_bag.setting_properties is not None:
|
|
if 'cache' in config_bag.setting_properties and \
|
|
'expire' in config_bag.setting_properties:
|
|
ntime = int(time())
|
|
else:
|
|
ntime = None
|
|
if 'cache' in config_bag.setting_properties and self._p_.hascache(path,
|
|
index):
|
|
is_cached, props = self._p_.getcache(path,
|
|
ntime,
|
|
index)
|
|
if not is_cached:
|
|
meta = self._getcontext().cfgimpl_get_meta()
|
|
if meta is None:
|
|
props = self._p_.getproperties(path,
|
|
opt.impl_getproperties())
|
|
else:
|
|
props = meta.cfgimpl_get_settings().getproperties(path,
|
|
index,
|
|
config_bag,
|
|
apply_requires)
|
|
if apply_requires:
|
|
props |= self.apply_requires(path,
|
|
opt.impl_getrequires(),
|
|
index,
|
|
False,
|
|
config_bag,
|
|
opt.impl_get_display_name())
|
|
props -= self.getpermissive(opt,
|
|
path)
|
|
if apply_requires and config_bag.setting_properties is not None and \
|
|
'cache' in config_bag.setting_properties:
|
|
if 'expire' in config_bag.setting_properties:
|
|
ntime = ntime + expires_time
|
|
self._p_.setcache(path,
|
|
props,
|
|
ntime,
|
|
index)
|
|
return props
|
|
|
|
def get_context_permissive(self):
|
|
return self.getpermissive(None, None)
|
|
|
|
def getpermissive(self,
|
|
opt,
|
|
path):
|
|
if opt and opt.impl_is_symlinkoption():
|
|
opt = opt.impl_getopt()
|
|
path = opt.impl_getpath(self._getcontext())
|
|
meta = self._getcontext().cfgimpl_get_meta()
|
|
if meta is not None:
|
|
return meta.cfgimpl_get_settings().getpermissive(opt,
|
|
path)
|
|
return self._pp_.getpermissive(path)
|
|
|
|
def apply_requires(self,
|
|
path,
|
|
current_requires,
|
|
index,
|
|
readable,
|
|
config_bag,
|
|
name):
|
|
"""carries out the jit (just in time) requirements between options
|
|
|
|
a requirement is a tuple of this form that comes from the option's
|
|
requirements validation::
|
|
|
|
(option, expected, action, inverse, transitive, same_action)
|
|
|
|
let's have a look at all the tuple's items:
|
|
|
|
- **option** is the target option's
|
|
|
|
- **expected** is the target option's value that is going to trigger
|
|
an action
|
|
|
|
- **action** is the (property) action to be accomplished if the target
|
|
option happens to have the expected value
|
|
|
|
- if **inverse** is `True` and if the target option's value does not
|
|
apply, then the property action must be removed from the option's
|
|
properties list (wich means that the property is inverted)
|
|
|
|
- **transitive**: but what happens if the target option cannot be
|
|
accessed ? We don't kown the target option's value. Actually if some
|
|
property in the target option is not present in the permissive, the
|
|
target option's value cannot be accessed. In this case, the
|
|
**action** have to be applied to the option. (the **action** property
|
|
is then added to the option).
|
|
|
|
- **same_action**: actually, if **same_action** is `True`, the
|
|
transitivity is not accomplished. The transitivity is accomplished
|
|
only if the target option **has the same property** that the demanded
|
|
action. If the target option's value is not accessible because of
|
|
another reason, because of a property of another type, then an
|
|
exception :exc:`~error.RequirementError` is raised.
|
|
|
|
And at last, if no target option matches the expected values, the
|
|
action will not add to the option's properties list.
|
|
|
|
:param opt: the option on wich the requirement occurs
|
|
:type opt: `option.Option()`
|
|
:param path: the option's path in the config
|
|
:type path: str
|
|
"""
|
|
#current_requires = opt.impl_getrequires()
|
|
|
|
# filters the callbacks
|
|
if readable:
|
|
calc_properties = {}
|
|
else:
|
|
calc_properties = set()
|
|
|
|
if not current_requires:
|
|
return calc_properties
|
|
|
|
context = self._getcontext()
|
|
all_properties = None
|
|
for requires in current_requires:
|
|
for require in requires:
|
|
exps, action, inverse, transitive, same_action, operator = require
|
|
breaked = False
|
|
for option, expected in exps:
|
|
reqpath = option.impl_getpath(context)
|
|
#FIXME c'est un peut tard !
|
|
if reqpath.startswith(path + '.'):
|
|
raise RequirementError(_("malformed requirements "
|
|
"imbrication detected for option:"
|
|
" '{0}' with requirement on: "
|
|
"'{1}'").format(path, reqpath))
|
|
idx = None
|
|
is_indexed = False
|
|
if option.impl_is_master_slaves('slave'):
|
|
idx = index
|
|
elif option.impl_is_multi():
|
|
is_indexed = True
|
|
sconfig_bag = config_bag.copy('nooption')
|
|
if config_bag.option == option:
|
|
sconfig_bag.setting_properties = None
|
|
sconfig_bag.force_unrestraint= False
|
|
sconfig_bag.validate = False
|
|
else:
|
|
sconfig_bag.force_permissive = True
|
|
sconfig_bag.option = option
|
|
try:
|
|
value = context.getattr(reqpath,
|
|
idx,
|
|
sconfig_bag)
|
|
if is_indexed:
|
|
value = value[index]
|
|
except PropertiesOptionError as err:
|
|
properties = err.proptype
|
|
if not transitive:
|
|
if all_properties is None:
|
|
all_properties = []
|
|
for requires_ in current_requires:
|
|
for require_ in requires_:
|
|
all_properties.append(require_[1])
|
|
if not set(properties) - set(all_properties):
|
|
continue
|
|
if same_action and action not in properties:
|
|
if len(properties) == 1:
|
|
prop_msg = _('property')
|
|
else:
|
|
prop_msg = _('properties')
|
|
raise RequirementError(_('cannot access to option "{0}" because '
|
|
'required option "{1}" has {2} {3}'
|
|
'').format(name,
|
|
option.impl_get_display_name(),
|
|
prop_msg,
|
|
display_list(list(properties), add_quote=True)))
|
|
# transitive action, add action
|
|
if operator != 'and':
|
|
if readable:
|
|
for msg in self.apply_requires(err._path,
|
|
err._requires,
|
|
err._index,
|
|
True,
|
|
err._config_bag,
|
|
err._name).values():
|
|
calc_properties.setdefault(action, []).extend(msg)
|
|
else:
|
|
calc_properties.add(action)
|
|
breaked = True
|
|
break
|
|
else:
|
|
if (not inverse and value in expected or
|
|
inverse and value not in expected):
|
|
if operator != 'and':
|
|
if readable:
|
|
if not inverse:
|
|
msg = _('the value of "{0}" is {1}')
|
|
else:
|
|
msg = _('the value of "{0}" is not {1}')
|
|
calc_properties.setdefault(action, []).append(
|
|
msg.format(option.impl_get_display_name(),
|
|
display_list(expected, 'or', add_quote=True)))
|
|
else:
|
|
calc_properties.add(action)
|
|
breaked = True
|
|
break
|
|
elif operator == 'and':
|
|
break
|
|
else:
|
|
if operator == 'and':
|
|
calc_properties.add(action)
|
|
continue # pragma: no cover
|
|
if breaked:
|
|
break
|
|
return calc_properties
|
|
|
|
#____________________________________________________________
|
|
# set methods
|
|
def set_context_properties(self,
|
|
properties):
|
|
self.setproperties(None,
|
|
properties,
|
|
None)
|
|
|
|
def setproperties(self,
|
|
path,
|
|
properties,
|
|
config_bag):
|
|
"""save properties for specified path
|
|
(never save properties if same has option properties)
|
|
"""
|
|
if self._getcontext().cfgimpl_get_meta() is not None:
|
|
raise ConfigError(_('cannot change property with metaconfig'))
|
|
if path is not None and config_bag.option.impl_getrequires() is not None:
|
|
not_allowed_props = properties & getattr(config_bag.option, '_calc_properties', static_set)
|
|
if not_allowed_props:
|
|
raise ValueError(_('cannot set property {} for option "{}" this property is calculated'
|
|
'').format(display_list(list(not_allowed_props), add_quote=True),
|
|
config_bag.option.impl_get_display_name()))
|
|
if config_bag is None:
|
|
opt = None
|
|
else:
|
|
opt = config_bag.option
|
|
if opt and opt.impl_is_symlinkoption():
|
|
raise TypeError(_("can't assign property to the symlinkoption \"{}\""
|
|
"").format(opt.impl_get_display_name()))
|
|
if 'force_default_on_freeze' in properties and \
|
|
'frozen' not in properties and \
|
|
opt.impl_is_master_slaves('master'):
|
|
raise ConfigError(_('a master ({0}) cannot have '
|
|
'"force_default_on_freeze" property without "frozen"'
|
|
'').format(opt.impl_get_display_name()))
|
|
self._p_.setproperties(path,
|
|
properties)
|
|
#values too because of slave values could have a PropertiesOptionError has value
|
|
self._getcontext().cfgimpl_reset_cache(opt,
|
|
path,
|
|
config_bag)
|
|
|
|
def set_context_permissive(self,
|
|
permissive):
|
|
self.setpermissive(None,
|
|
None,
|
|
None,
|
|
permissive)
|
|
|
|
def setpermissive(self,
|
|
opt,
|
|
path,
|
|
config_bag,
|
|
permissives):
|
|
"""
|
|
enables us to put the permissives in the storage
|
|
|
|
:param path: the option's path
|
|
:param type: str
|
|
:param opt: if an option object is set, the path is extracted.
|
|
it is better (faster) to set the path parameter
|
|
instead of passing a :class:`tiramisu.option.Option()` object.
|
|
"""
|
|
if self._getcontext().cfgimpl_get_meta() is not None:
|
|
raise ConfigError(_('cannot change permissive with metaconfig'))
|
|
if not isinstance(permissives, frozenset):
|
|
raise TypeError(_('permissive must be a frozenset'))
|
|
if opt and opt.impl_is_symlinkoption():
|
|
raise TypeError(_("can't assign permissive to the symlinkoption \"{}\""
|
|
"").format(opt.impl_get_display_name()))
|
|
forbidden_permissives = FORBIDDEN_SET_PERMISSIVES & permissives
|
|
if forbidden_permissives:
|
|
raise ConfigError(_('cannot add those permissives: {0}').format(
|
|
' '.join(forbidden_permissives)))
|
|
self._pp_.setpermissive(path, permissives)
|
|
self._getcontext().cfgimpl_reset_cache(opt,
|
|
path,
|
|
config_bag)
|
|
|
|
#____________________________________________________________
|
|
# reset methods
|
|
|
|
def reset(self,
|
|
opt,
|
|
path,
|
|
config_bag,
|
|
all_properties=False):
|
|
if self._getcontext().cfgimpl_get_meta() is not None:
|
|
raise ConfigError(_('cannot change property with metaconfig'))
|
|
if opt and opt.impl_is_symlinkoption():
|
|
raise TypeError(_("can't reset properties to the symlinkoption \"{}\""
|
|
"").format(opt.impl_get_display_name()))
|
|
if all_properties and (path or opt):
|
|
raise ValueError(_('opt and all_properties must not be set '
|
|
'together in reset'))
|
|
if all_properties:
|
|
self._p_.reset_all_properties()
|
|
else:
|
|
if opt is not None and path is None:
|
|
path = opt.impl_getpath(self._getcontext())
|
|
self._p_.delproperties(path)
|
|
self._getcontext().cfgimpl_reset_cache(opt,
|
|
path,
|
|
config_bag)
|
|
|
|
#____________________________________________________________
|
|
# validate properties
|
|
|
|
def validate_properties(self,
|
|
path,
|
|
index,
|
|
config_bag):
|
|
"""
|
|
validation upon the properties related to `opt`
|
|
|
|
:param opt: an option or an option description object
|
|
:param force_permissive: behaves as if the permissive property
|
|
was present
|
|
"""
|
|
opt = config_bag.option
|
|
|
|
# calc properties
|
|
self_properties = config_bag.properties
|
|
if self_properties is None:
|
|
self_properties = self.getproperties(path,
|
|
index,
|
|
config_bag)
|
|
config_bag.properties = self_properties
|
|
properties = self_properties & config_bag.setting_properties - {'frozen', 'mandatory', 'empty'}
|
|
|
|
# remove permissive properties
|
|
if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and properties:
|
|
# remove global permissive if need
|
|
properties -= self.get_context_permissive()
|
|
# at this point an option should not remain in properties
|
|
if properties != frozenset():
|
|
raise PropertiesOptionError(path,
|
|
index,
|
|
config_bag,
|
|
properties,
|
|
self)
|
|
|
|
def validate_mandatory(self,
|
|
path,
|
|
index,
|
|
value,
|
|
config_bag):
|
|
values = self._getcontext().cfgimpl_get_values()
|
|
opt = config_bag.option
|
|
is_mandatory = False
|
|
if config_bag.setting_properties and 'mandatory' in config_bag.setting_properties:
|
|
if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and \
|
|
'mandatory' in self.get_context_permissive():
|
|
pass
|
|
elif 'mandatory' in config_bag.properties and values.isempty(opt,
|
|
value,
|
|
index=index):
|
|
is_mandatory = True
|
|
if 'empty' in config_bag.properties and values.isempty(opt,
|
|
value,
|
|
force_allow_empty_list=True,
|
|
index=index):
|
|
is_mandatory = True
|
|
if is_mandatory:
|
|
raise PropertiesOptionError(path,
|
|
index,
|
|
config_bag,
|
|
['mandatory'],
|
|
self)
|
|
|
|
def validate_frozen(self,
|
|
path,
|
|
index,
|
|
config_bag):
|
|
if config_bag.setting_properties and \
|
|
('everything_frozen' in config_bag.setting_properties or
|
|
'frozen' in config_bag.properties) and \
|
|
not ((config_bag.force_permissive is True or
|
|
'permissive' in config_bag.setting_properties) and
|
|
'frozen' in self.get_context_permissive()):
|
|
raise PropertiesOptionError(path,
|
|
index,
|
|
config_bag,
|
|
['frozen'],
|
|
self)
|
|
return False
|
|
#____________________________________________________________
|
|
# read only/read write
|
|
|
|
def _read(self,
|
|
remove,
|
|
append):
|
|
props = self._p_.getproperties(None,
|
|
default_properties)
|
|
modified = False
|
|
if remove & props:
|
|
props = props - remove
|
|
modified = True
|
|
if append & props != append:
|
|
props = props | append
|
|
modified = True
|
|
if modified:
|
|
self.set_context_properties(frozenset(props))
|
|
|
|
def read_only(self):
|
|
"convenience method to freeze, hide and disable"
|
|
self._read(ro_remove,
|
|
ro_append)
|
|
|
|
def read_write(self):
|
|
"convenience method to freeze, hide and disable"
|
|
self._read(rw_remove,
|
|
rw_append)
|
|
|
|
#____________________________________________________________
|
|
# default owner methods
|
|
|
|
def setowner(self,
|
|
owner):
|
|
":param owner: sets the default value for owner at the Config level"
|
|
self._owner = owner
|
|
|
|
def getowner(self):
|
|
return self._owner
|