tiramisu/tiramisu/setting.py

796 lines
31 KiB
Python
Raw Normal View History

2012-11-19 10:45:03 +01:00
# -*- coding: utf-8 -*-
"sets the options of the configuration objects Config object itself"
2018-01-26 07:33:47 +01:00
# Copyright (C) 2012-2018 Team tiramisu (see AUTHORS for all contributors)
2012-11-19 10:45:03 +01:00
#
2013-09-22 22:33:09 +02:00
# 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.
2012-11-19 10:45:03 +01:00
#
2013-09-22 22:33:09 +02:00
# 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.
2012-11-19 10:45:03 +01:00
#
2013-09-22 22:33:09 +02:00
# 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/>.
2012-11-19 10:45:03 +01:00
# ____________________________________________________________
from time import time
from copy import copy
2014-04-03 22:15:41 +02:00
from logging import getLogger
import weakref
from .error import (RequirementError, PropertiesOptionError,
2016-09-14 20:17:25 +02:00
ConstError, ConfigError, display_list)
from .i18n import _
2013-09-07 21:47:17 +02:00
"Default encoding for display a Config if raise UnicodeEncodeError"
default_encoding = 'utf-8'
2013-09-07 21:47:17 +02:00
"""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
2013-09-07 21:47:17 +02:00
"""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
2013-09-07 21:47:17 +02:00
validator
launch validator set by user in option (this property has no effect
for internal validator)
2015-04-18 23:11:57 +02:00
warnings
display warnings during validation
2013-09-07 21:47:17 +02:00
"""
2017-12-23 20:33:36 +01:00
default_properties = ('cache', 'validator', 'warnings')
2013-09-07 21:47:17 +02:00
"""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
"""
2013-09-06 09:05:19 +02:00
ro_append = set(['frozen', 'disabled', 'validator', 'everything_frozen',
'mandatory', 'empty'])
2013-09-07 21:47:17 +02:00
ro_remove = set(['permissive', 'hidden'])
2013-09-06 09:05:19 +02:00
rw_append = set(['frozen', 'disabled', 'validator', 'hidden'])
rw_remove = set(['permissive', 'everything_frozen', 'mandatory', 'empty'])
2017-12-23 20:21:07 +01:00
FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value'])
2017-12-28 11:47:29 +01:00
FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze'])
2014-07-06 15:31:57 +02:00
2014-04-03 22:15:41 +02:00
log = getLogger('tiramisu')
#FIXME
#import logging
#logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
2016-09-14 20:17:25 +02:00
debug = False
2017-07-22 16:26:06 +02:00
static_set = frozenset()
2014-04-03 22:15:41 +02:00
2017-12-19 23:11:45 +01:00
class ConfigBag(object):
__slots__ = ('default',
'config',
'option',
'ori_option',
'properties',
'validate',
'setting_properties',
'force_permissive',
'force_unrestraint',
'display_warnings',
'trusted_cached_properties',
'fromconsistency',
)
def __init__(self, config, **kwargs):
self.default = {'force_permissive': False,
'force_unrestraint': False,
'display_warnings': True,
'trusted_cached_properties': True,
}
self.config = config
2017-12-23 10:40:41 +01:00
self.fromconsistency = []
2017-12-19 23:11:45 +01:00
for key, value in kwargs.items():
if value != self.default.get(key):
setattr(self, key, value)
def __getattr__(self, key):
2018-01-03 21:07:51 +01:00
if key in ['validate', 'validate_properties']:
return not self.force_unrestraint
2017-12-19 23:11:45 +01:00
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
return self.default.get(key)
def copy(self, filters='all'):
kwargs = {}
for key in self.__slots__:
if filters == 'nooption' and (key.startswith('option') or \
key == 'properties'):
continue
2017-12-23 10:40:41 +01:00
if key == 'fromconsistency':
kwargs['fromconsistency'] = copy(self.fromconsistency)
elif key != 'default':
2017-12-19 23:11:45 +01:00
value = getattr(self, key)
if value != self.default.get(key):
kwargs[key] = value
return ConfigBag(**kwargs)
2013-09-07 21:47:17 +02:00
# ____________________________________________________________
2013-09-22 20:57:52 +02:00
class _NameSpace(object):
2012-12-06 18:14:57 +01:00
"""convenient class that emulates a module
2013-09-07 21:47:17 +02:00
and builds constants (that is, unique names)
when attribute is added, we cannot delete it
"""
2012-12-06 18:14:57 +01:00
2017-11-20 17:01:36 +01:00
def __setattr__(self,
name,
value):
if name in self.__dict__:
raise ConstError(_("can't rebind {0}").format(name))
2012-12-06 18:14:57 +01:00
self.__dict__[name] = value
2017-11-20 17:01:36 +01:00
def __delattr__(self,
name):
raise ConstError(_("can't unbind {0}").format(name))
2013-04-03 12:20:26 +02:00
class GroupModule(_NameSpace):
2012-12-10 09:53:13 +01:00
"emulates a module to manage unique group (OptionDescription) names"
2012-12-10 14:38:25 +01:00
class GroupType(str):
2012-12-06 18:14:57 +01:00
"""allowed normal group (OptionDescription) names
*normal* means : groups that are not master
"""
pass
2013-04-03 12:20:26 +02:00
2012-12-10 14:38:25 +01:00
class DefaultGroupType(GroupType):
2012-12-10 14:10:05 +01:00
"""groups that are default (typically 'default')"""
pass
2012-12-10 14:38:25 +01:00
class MasterGroupType(GroupType):
2012-12-06 18:14:57 +01:00
"""allowed normal group (OptionDescription) names
*master* means : groups that have the 'master' attribute set
"""
pass
2013-04-03 12:20:26 +02:00
class OwnerModule(_NameSpace):
2012-12-10 14:10:05 +01:00
"""emulates a module to manage unique owner names.
owners are living in `Config._cfgimpl_value_owners`
"""
class Owner(str):
"""allowed owner names
"""
pass
2013-04-03 12:20:26 +02:00
2012-12-10 14:10:05 +01:00
class DefaultOwner(Owner):
"""groups that are default (typically 'default')"""
pass
2013-09-07 21:47:17 +02:00
2017-11-20 17:01:36 +01:00
def addowner(self, name):
"""
:param name: the name of the new owner
"""
setattr(owners, name, owners.Owner(name))
2013-09-07 21:47:17 +02:00
# ____________________________________________________________
2017-11-20 17:01:36 +01:00
# populate groups
groups = GroupModule()
"""groups.default
default group set when creating a new optiondescription"""
groups.default = groups.DefaultGroupType('default')
2013-09-07 21:47:17 +02:00
2017-11-20 17:01:36 +01:00
"""groups.master
2013-09-07 21:47:17 +02:00
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
2017-11-20 17:01:36 +01:00
option"""
groups.master = groups.MasterGroupType('master')
2013-09-07 21:47:17 +02:00
2017-11-20 17:01:36 +01:00
""" groups.family
example of group, no special behavior with this group's type"""
groups.family = groups.GroupType('family')
2013-04-03 12:20:26 +02:00
2012-12-10 14:38:25 +01:00
2013-09-07 21:47:17 +02:00
# ____________________________________________________________
2017-11-20 17:01:36 +01:00
# populate owners with default attributes
2013-09-07 21:47:17 +02:00
owners = OwnerModule()
2017-11-20 17:01:36 +01:00
"""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')
2017-11-23 16:56:14 +01:00
"""meta
special owner when value comes from metaconfig"""
owners.meta = owners.Owner('meta')
forbidden_owners = (owners.default, owners.forced, owners.meta)
2013-02-21 17:07:00 +01:00
2013-04-03 12:20:26 +02:00
# ____________________________________________________________
2014-04-25 22:57:08 +02:00
class Undefined(object):
2018-04-07 20:15:19 +02:00
def __str__(self):
return 'Undefined'
__repr__ = __str__
undefined = Undefined()
2012-12-10 14:10:05 +01:00
#____________________________________________________________
2013-08-20 09:47:12 +02:00
class Settings(object):
2014-01-06 15:32:28 +01:00
"``config.Config()``'s configuration options settings"
2017-07-13 22:04:06 +02:00
__slots__ = ('context', '_owner', '_p_', '_pp_', '__weakref__')
2013-04-03 12:20:26 +02:00
2017-07-13 22:04:06 +02:00
def __init__(self, context, properties, permissives):
2013-08-21 17:21:09 +02:00
"""
2013-08-21 18:34:32 +02:00
initializer
2013-08-21 17:21:09 +02:00
:param context: the root config
2013-08-21 18:34:32 +02:00
:param storage: the storage type
2013-08-21 23:21:28 +02:00
- dictionary -> in memory
2013-08-21 17:21:09 +02:00
- sqlite3 -> persistent
"""
2013-04-03 12:20:26 +02:00
# generic owner
self._owner = owners.user
self.context = weakref.ref(context)
2017-07-13 22:04:06 +02:00
self._p_ = properties
self._pp_ = permissives
2013-08-20 09:47:12 +02:00
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()
2014-06-19 23:22:39 +02:00
if context is None: # pragma: optional cover
raise ConfigError(_('the context does not exist anymore'))
return context
2012-12-11 11:18:53 +01:00
#____________________________________________________________
2017-11-20 17:01:36 +01:00
# get properties and permissive methods
2013-03-14 11:31:44 +01:00
def get_context_properties(self):
2017-11-20 17:01:36 +01:00
ntime = int(time())
if self._p_.hascache(None,
None):
is_cached, props = self._p_.getcache(None,
ntime,
None)
2013-04-03 12:20:26 +02:00
else:
2015-05-03 09:56:03 +02:00
is_cached = False
2017-11-20 17:01:36 +01:00
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
2015-10-29 09:03:13 +01:00
else:
ntime = None
2017-11-20 17:01:36 +01:00
self._p_.setcache(None, props, ntime, None)
2015-10-29 09:03:13 +01:00
return props
2013-03-14 11:31:44 +01:00
2017-11-20 17:01:36 +01:00
def getproperties(self,
path,
2017-12-19 23:11:45 +01:00
index,
2018-01-26 07:33:47 +01:00
config_bag,
apply_requires=True):
2017-10-22 09:48:08 +02:00
"""
2013-04-03 12:20:26 +02:00
"""
2017-12-19 23:11:45 +01:00
opt = config_bag.option
2017-12-04 20:05:36 +01:00
if opt.impl_is_symlinkoption():
opt = opt.impl_getopt()
path = opt.impl_getpath(self._getcontext())
2017-11-20 17:01:36 +01:00
is_cached = False
2018-01-26 07:33:47 +01:00
if apply_requires and config_bag.setting_properties is not None:
2017-12-19 23:11:45 +01:00
if 'cache' in config_bag.setting_properties and \
'expire' in config_bag.setting_properties:
2017-11-20 17:01:36 +01:00
ntime = int(time())
2017-07-13 22:04:06 +02:00
else:
2017-11-20 17:01:36 +01:00
ntime = None
2017-12-19 23:11:45 +01:00
if 'cache' in config_bag.setting_properties and self._p_.hascache(path,
index):
2017-11-20 17:01:36 +01:00
is_cached, props = self._p_.getcache(path,
ntime,
index)
if not is_cached:
2017-11-23 16:56:14 +01:00
meta = self._getcontext().cfgimpl_get_meta()
if meta is None:
props = self._p_.getproperties(path,
opt.impl_getproperties())
else:
2018-01-03 21:07:51 +01:00
props = meta.cfgimpl_get_settings().getproperties(path,
2017-12-19 23:11:45 +01:00
index,
2018-01-26 07:33:47 +01:00
config_bag,
apply_requires)
if apply_requires:
props |= self.apply_requires(path,
index,
False,
config_bag)
2017-12-04 20:05:36 +01:00
props -= self.getpermissive(opt,
path)
2018-01-26 07:33:47 +01:00
if apply_requires and config_bag.setting_properties is not None and \
2017-12-19 23:11:45 +01:00
'cache' in config_bag.setting_properties:
if 'expire' in config_bag.setting_properties:
2017-07-13 22:04:06 +02:00
ntime = ntime + expires_time
2017-11-20 17:01:36 +01:00
self._p_.setcache(path,
props,
ntime,
index)
return props
2017-11-20 17:01:36 +01:00
def get_context_permissive(self):
2017-12-04 20:05:36 +01:00
return self.getpermissive(None, None)
2012-11-19 10:45:03 +01:00
2017-11-20 17:01:36 +01:00
def getpermissive(self,
2017-12-04 20:05:36 +01:00
opt,
2017-11-20 17:01:36 +01:00
path):
2017-12-04 20:05:36 +01:00
if opt and opt.impl_is_symlinkoption():
opt = opt.impl_getopt()
path = opt.impl_getpath(self._getcontext())
2017-11-23 16:56:14 +01:00
meta = self._getcontext().cfgimpl_get_meta()
2017-12-13 22:15:34 +01:00
if meta is not None:
return meta.cfgimpl_get_settings().getpermissive(opt,
path)
return self._pp_.getpermissive(path)
2017-11-20 17:01:36 +01:00
def apply_requires(self,
path,
index,
2017-12-19 23:11:45 +01:00
debug,
config_bag):
"""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:
2013-09-07 22:16:50 +02:00
- **option** is the target option's
2013-09-07 22:16:50 +02:00
- **expected** is the target option's value that is going to trigger
an action
2013-09-07 22:16:50 +02:00
- **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
"""
2017-12-19 23:11:45 +01:00
opt = config_bag.option
2016-10-23 09:38:35 +02:00
current_requires = opt.impl_getrequires()
# filters the callbacks
2016-09-14 20:17:25 +02:00
if debug:
calc_properties = {}
else:
calc_properties = set()
2016-10-23 09:38:35 +02:00
if not current_requires:
return calc_properties
context = self._getcontext()
all_properties = None
2016-10-23 09:38:35 +02:00
for requires in current_requires:
for require in requires:
2017-05-20 16:28:19 +02:00
exps, action, inverse, \
transitive, same_action, operator = require
breaked = False
for exp in exps:
option, expected = exp
reqpath = option.impl_getpath(context)
if reqpath == path or reqpath.startswith(path + '.'): # pragma: optional cover
raise RequirementError(_("malformed requirements "
"imbrication detected for option:"
" '{0}' with requirement on: "
"'{1}'").format(path, reqpath))
2017-12-29 11:38:41 +01:00
if not option.impl_is_multi():
idx = None
is_indexed = False
elif option.impl_is_master_slaves('slave'):
2017-05-20 16:28:19 +02:00
idx = index
2017-12-29 11:38:41 +01:00
is_indexed = False
2015-11-30 15:55:34 +01:00
else:
2017-05-20 16:28:19 +02:00
idx = None
2017-12-29 11:38:41 +01:00
is_indexed = True
2017-12-19 23:11:45 +01:00
sconfig_bag = config_bag.copy('nooption')
sconfig_bag.option = option
2018-03-31 21:17:37 +02:00
sconfig_bag.force_permissive = True
2017-11-13 22:45:53 +01:00
try:
value = context.getattr(reqpath,
2017-12-19 23:11:45 +01:00
idx,
sconfig_bag)
2017-12-29 11:38:41 +01:00
if is_indexed:
value = value[index]
2017-11-13 22:45:53 +01:00
except PropertiesOptionError as err:
if not transitive:
if all_properties is None:
all_properties = []
2017-11-20 17:01:36 +01:00
for requires_ in opt.impl_getrequires():
for require_ in requires_:
all_properties.append(require_[1])
2017-11-13 22:45:53 +01:00
if not set(err.proptype) - set(all_properties):
continue
properties = err.proptype
if same_action and action not in properties: # pragma: optional cover
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(opt.impl_get_display_name(),
option.impl_get_display_name(),
prop_msg,
2017-12-29 11:38:41 +01:00
display_list(list(properties))))
2017-11-13 22:45:53 +01:00
orig_value = err
# transitive action, force expected
value = expected[0]
inverse = False
2016-09-14 20:17:25 +02:00
else:
2017-05-20 16:28:19 +02:00
orig_value = value
if (not inverse and value in expected or
inverse and value not in expected):
if operator != 'and':
if debug:
if isinstance(orig_value, PropertiesOptionError):
2017-12-23 20:21:07 +01:00
for msg in orig_value._settings.apply_requires(**orig_value._datas).values():
2017-05-20 16:28:19 +02:00
calc_properties.setdefault(action, []).extend(msg)
else:
if not inverse:
2017-12-23 20:21:07 +01:00
msg = _('the value of "{0}" is {1}')
2017-05-20 16:28:19 +02:00
else:
2017-12-23 20:21:07 +01:00
msg = _('the value of "{0}" is not {1}')
2017-05-20 16:28:19 +02:00
calc_properties.setdefault(action, []).append(
msg.format(option.impl_get_display_name(),
2017-12-23 20:21:07 +01:00
display_list(expected, 'or', add_quote=True)))
2017-05-20 16:28:19 +02:00
else:
calc_properties.add(action)
breaked = True
break
elif operator == 'and':
2016-09-14 20:17:25 +02:00
break
2017-05-20 16:28:19 +02:00
else:
if operator == 'and':
calc_properties.add(action)
2017-07-21 22:34:41 +02:00
continue # pragma: no cover
2017-05-20 16:28:19 +02:00
if breaked:
break
return calc_properties
2013-08-19 11:01:21 +02:00
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# set methods
2017-12-19 23:11:45 +01:00
def set_context_properties(self,
properties):
self.setproperties(None,
properties,
None)
2017-11-20 17:01:36 +01:00
def setproperties(self,
path,
2017-12-19 23:11:45 +01:00
properties,
config_bag):
2017-11-20 17:01:36 +01:00
"""save properties for specified path
(never save properties if same has option properties)
"""
if self._getcontext().cfgimpl_get_meta() is not None:
2017-11-23 16:56:14 +01:00
raise ConfigError(_('cannot change property with metaconfig'))
2017-12-29 11:38:41 +01:00
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:
if len(not_allowed_props) == 1:
prop_msg = _('property')
calc_msg = _('this property is calculated')
else:
prop_msg = _('properties')
calc_msg = _('those properties are calculated')
2018-03-19 08:33:53 +01:00
raise ValueError(_('cannot set {} {} for option "{}" {}'
2017-12-29 11:38:41 +01:00
'').format(prop_msg, display_list(list(not_allowed_props), add_quote=True),
2018-03-19 08:33:53 +01:00
config_bag.option.impl_get_display_name(),
2017-12-29 11:38:41 +01:00
calc_msg))
2017-12-19 23:11:45 +01:00
if config_bag is None:
opt = None
else:
opt = config_bag.option
2017-12-04 20:05:36 +01:00
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't assign properties 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()))
2017-11-20 17:01:36 +01:00
if not isinstance(properties, frozenset):
raise TypeError(_('properties must be a frozenset'))
self._p_.setproperties(path,
properties)
#values too because of slave values could have a PropertiesOptionError has value
2018-04-09 21:37:49 +02:00
self._getcontext().cfgimpl_reset_cache(opt,
path,
config_bag)
2017-11-20 17:01:36 +01:00
2018-04-09 21:37:49 +02:00
def set_context_permissive(self,
permissive):
self.setpermissive(None,
None,
None,
permissive)
2017-11-20 17:01:36 +01:00
def setpermissive(self,
opt,
path,
2018-04-09 21:37:49 +02:00
config_bag,
2017-11-20 17:01:36 +01:00
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.
"""
2017-11-23 16:56:14 +01:00
if self._getcontext().cfgimpl_get_meta() is not None:
raise ConfigError(_('cannot change permissive with metaconfig'))
2017-11-20 17:01:36 +01:00
if not isinstance(permissives, frozenset):
raise TypeError(_('permissive must be a frozenset'))
2017-12-04 20:05:36 +01:00
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't assign permissive to the SymLinkOption \"{}\""
"").format(opt.impl_get_display_name()))
2017-12-28 11:47:29 +01:00
forbidden_permissives = FORBIDDEN_SET_PERMISSIVES & permissives
2017-11-20 17:01:36 +01:00
if forbidden_permissives:
raise ConfigError(_('cannot add those permissives: {0}').format(
' '.join(forbidden_permissives)))
self._pp_.setpermissive(path, permissives)
2018-04-09 21:37:49 +02:00
self._getcontext().cfgimpl_reset_cache(opt,
path,
config_bag)
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# reset methods
2017-11-23 16:56:14 +01:00
def reset(self,
2018-04-09 21:37:49 +02:00
opt,
path,
config_bag,
2017-11-23 16:56:14 +01:00
all_properties=False):
if self._getcontext().cfgimpl_get_meta() is not None:
raise ConfigError(_('cannot change property with metaconfig'))
2017-12-04 20:05:36 +01:00
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): # pragma: optional cover
2017-11-20 17:01:36 +01:00
raise ValueError(_('opt and all_properties must not be set '
'together in reset'))
if all_properties:
self._p_.reset_all_properties()
else:
2017-12-04 20:05:36 +01:00
if opt is not None and path is None:
path = opt.impl_getpath(self._getcontext())
self._p_.delproperties(path)
2018-04-09 21:37:49 +02:00
self._getcontext().cfgimpl_reset_cache(opt,
path,
config_bag)
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# validate properties
def validate_properties(self,
path,
2017-12-19 23:11:45 +01:00
index,
config_bag):
2017-11-20 17:01:36 +01:00
"""
2017-12-05 21:49:19 +01:00
validation upon the properties related to `opt`
2017-11-20 17:01:36 +01:00
2017-12-05 21:49:19 +01:00
:param opt: an option or an option description object
2017-11-20 17:01:36 +01:00
:param force_permissive: behaves as if the permissive property
was present
"""
2017-12-19 23:11:45 +01:00
opt = config_bag.option
2017-12-05 21:49:19 +01:00
2017-11-20 17:01:36 +01:00
# calc properties
2017-12-19 23:11:45 +01:00
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'}
2017-12-05 21:49:19 +01:00
if not opt.impl_is_optiondescription():
2017-11-23 16:56:14 +01:00
opt_type = 'option'
else:
opt_type = 'optiondescription'
2017-11-20 17:01:36 +01:00
2017-12-05 21:49:19 +01:00
2017-11-20 17:01:36 +01:00
# remove permissive properties
2017-12-28 11:47:29 +01:00
if (config_bag.force_permissive is True or 'permissive' in config_bag.setting_properties) and properties:
2017-11-20 17:01:36 +01:00
# remove global permissive if need
properties -= self.get_context_permissive()
# at this point an option should not remain in properties
if properties != frozenset():
2017-12-19 23:11:45 +01:00
datas = {'path': path,
'config_bag': config_bag,
2017-11-20 17:01:36 +01:00
'index': index,
'debug': True}
2017-11-23 16:56:14 +01:00
raise PropertiesOptionError(None,
properties,
self,
datas,
opt_type)
2017-11-28 22:42:30 +01:00
def validate_mandatory(self,
2017-12-19 23:11:45 +01:00
path,
2017-11-28 22:42:30 +01:00
index,
value,
2017-12-19 23:11:45 +01:00
config_bag):
2017-11-23 16:56:14 +01:00
values = self._getcontext().cfgimpl_get_values()
2017-12-19 23:11:45 +01:00
opt = config_bag.option
2017-12-28 11:47:29 +01:00
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,
2017-12-19 23:11:45 +01:00
value,
force_allow_empty_list=True,
2017-12-28 11:47:29 +01:00
index=index):
is_mandatory = True
if is_mandatory:
2017-12-19 23:11:45 +01:00
datas = {'path': path,
'config_bag': config_bag,
'index': index,
'debug': True}
raise PropertiesOptionError(None,
['mandatory'],
self,
datas,
'option')
2017-11-20 17:01:36 +01:00
2017-12-13 22:15:34 +01:00
def validate_frozen(self,
2017-12-19 23:11:45 +01:00
config_bag):
2017-12-28 11:47:29 +01:00
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()):
return True
return False
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# read only/read write
def _read(self,
remove,
append):
props = self._p_.getproperties(None,
default_properties)
modified = False
2017-12-28 11:47:29 +01:00
if remove & props:
2017-11-20 17:01:36 +01:00
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