tiramisu/tiramisu/setting.py

795 lines
32 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"
2020-01-22 20:46:18 +01:00
# Copyright (C) 2012-2020 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
# ____________________________________________________________
2019-11-19 18:39:44 +01:00
from itertools import chain
2019-10-27 11:09:15 +01:00
from .error import PropertiesOptionError, ConstError, ConfigError, LeadershipError, display_list
from .i18n import _
2019-12-24 15:24:20 +01:00
from .asyncinit import asyncinit
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
"""
2019-02-24 10:36:42 +01:00
EXPIRATION_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
* 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
2019-11-19 18:39:44 +01:00
mandatory
should set value for option with this properties if 'mandatory' is set in
config
example: 'a', ['a'], [None] are valid
None, [] are not valid
2019-11-19 18:39:44 +01:00
empty
raise mandatory PropertiesOptionError if multi or leader have empty value
example: ['a'] is valid
[None] is not valid
2019-11-19 18:39:44 +01:00
unique
raise ValueError if a value is set twice or more in a multi Option
2013-09-07 21:47:17 +02:00
* Special Config properties:
cache
if set, enable cache settings and values
expire
2019-02-24 10:36:42 +01:00
if set, settings and values in cache expire after ``expiration_time``
2013-09-07 21:47:17 +02:00
everything_frozen
whole option in config are frozen (even if option have not frozen
property)
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
demoting_error_warning
all value errors are convert to warning (ValueErrorWarning)
2013-09-07 21:47:17 +02:00
"""
DEFAULT_PROPERTIES = frozenset(['cache', 'validator', 'warnings'])
SPECIAL_PROPERTIES = {'frozen', 'mandatory', 'empty', 'force_store_value'}
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
"""
RO_APPEND = frozenset(['frozen', 'disabled', 'validator', 'everything_frozen',
'mandatory', 'empty', 'force_store_value'])
RO_REMOVE = frozenset(['permissive', 'hidden'])
RW_APPEND = frozenset(['frozen', 'disabled', 'validator', 'hidden',
'force_store_value'])
RW_REMOVE = frozenset(['permissive', 'everything_frozen', 'mandatory',
'empty'])
2017-12-23 20:21:07 +01:00
FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value'])
2019-02-21 19:33:39 +01:00
FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze',
'force_metaconfig_on_freeze',
2019-02-21 19:33:39 +01:00
'force_store_value'])
2019-10-27 11:09:15 +01:00
ALLOWED_LEADER_PROPERTIES = frozenset(['empty',
2019-11-19 18:39:44 +01:00
'unique',
'force_store_value',
'mandatory',
'force_default_on_freeze',
'force_metaconfig_on_freeze',
'frozen'])
2014-07-06 15:31:57 +02:00
2017-07-22 16:26:06 +02:00
static_set = frozenset()
2014-04-03 22:15:41 +02:00
2018-08-01 08:37:58 +02:00
class OptionBag:
__slots__ = ('option', # current option
'path',
'index',
'config_bag',
'ori_option', # original option (for example useful for symlinkoption)
'properties', # properties of current option
2019-06-12 08:45:56 +02:00
'properties_setted',
'apply_requires', # apply requires or not for this option
2018-08-01 08:37:58 +02:00
)
def __init__(self):
self.option = None
def set_option(self,
option,
index,
config_bag):
2019-12-25 20:44:56 +01:00
if config_bag is undefined:
self.path = None
else:
self.path = option.impl_getpath()
2018-08-01 08:37:58 +02:00
self.index = index
self.option = option
self.config_bag = config_bag
def __getattr__(self, key):
2019-12-24 15:24:20 +01:00
if key == 'ori_option':
2018-08-01 08:37:58 +02:00
return self.option
elif key == 'apply_requires':
return True
2019-06-12 08:45:56 +02:00
elif key == 'properties_setted':
return False
2019-12-24 15:24:20 +01:00
raise KeyError('unknown key "{}" for OptionBag'.format(key)) # pragma: no cover
2018-08-01 08:37:58 +02:00
2019-06-12 08:45:56 +02:00
def __setattr__(self, key, val):
super().__setattr__(key, val)
if key == 'properties':
self.properties_setted = True
2018-09-04 08:36:02 +02:00
def __delattr__(self, key):
2018-10-31 16:08:22 +01:00
if key in ['properties', 'permissives']:
try:
super().__delattr__(key)
except AttributeError:
pass
2018-09-04 08:36:02 +02:00
return
2019-12-24 15:24:20 +01:00
raise KeyError(_('cannot delete key "{}" for OptionBag').format(key)) # pragma: no cover
2018-09-04 08:36:02 +02:00
2018-08-18 10:03:08 +02:00
def copy(self):
option_bag = OptionBag()
for key in self.__slots__:
if key == 'properties' and self.config_bag is undefined:
continue
if hasattr(self, key):
setattr(option_bag, key, getattr(self, key))
2018-08-18 10:03:08 +02:00
return option_bag
2018-08-01 08:37:58 +02:00
class ConfigBag:
2018-08-18 07:51:04 +02:00
__slots__ = ('context', # link to the current context
'properties', # properties for current context
2018-12-24 09:30:58 +01:00
'true_properties', # properties for current context
2019-07-04 20:43:47 +02:00
'is_unrestraint',
2018-08-18 08:06:29 +02:00
'permissives', # permissives for current context
2020-01-22 20:46:18 +01:00
'expiration_time', # EXPIRATION_TIME
'connection',
)
2019-02-24 10:36:42 +01:00
2019-12-24 15:24:20 +01:00
def __init__(self,
context,
properties: set,
permissives: frozenset,
**kwargs):
2018-08-02 22:35:40 +02:00
self.context = context
2019-12-24 15:24:20 +01:00
self.properties = properties
self.permissives = permissives
2017-12-19 23:11:45 +01:00
for key, value in kwargs.items():
2018-08-01 08:37:58 +02:00
setattr(self, key, value)
2017-12-19 23:11:45 +01:00
def __getattr__(self, key):
2018-12-24 09:30:58 +01:00
if key == 'true_properties':
return self.properties
2019-02-24 10:36:42 +01:00
if key == 'expiration_time':
self.expiration_time = EXPIRATION_TIME
return self.expiration_time
2019-07-04 20:43:47 +02:00
if key == 'is_unrestraint':
return False
2019-12-24 15:24:20 +01:00
raise KeyError('unknown key "{}" for ConfigBag'.format(key)) # pragma: no cover
def __setattr__(self, key, value):
super().__setattr__(key, value)
2018-08-17 23:11:25 +02:00
def remove_warnings(self):
self.properties = frozenset(self.properties - {'warnings'})
2018-08-17 23:11:25 +02:00
def remove_validation(self):
2018-08-18 10:03:08 +02:00
self.properties = frozenset(self.properties - {'validator'})
2018-12-24 09:30:58 +01:00
def unrestraint(self):
2019-07-04 20:43:47 +02:00
self.is_unrestraint = True
2018-12-24 09:30:58 +01:00
self.true_properties = self.properties
self.properties = frozenset(['cache'])
2018-08-18 08:06:29 +02:00
def set_permissive(self):
self.properties = frozenset(self.properties | {'permissive'})
2018-08-01 08:37:58 +02:00
def copy(self):
2017-12-19 23:11:45 +01:00
kwargs = {}
for key in self.__slots__:
2020-01-22 20:46:18 +01:00
try:
kwargs[key] = getattr(self, key)
except KeyError:
pass
2017-12-19 23:11:45 +01:00
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
2019-02-23 19:06:23 +01:00
*normal* means : groups that are not leader
2012-12-06 18:14:57 +01:00
"""
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
2019-02-23 19:06:23 +01:00
class LeadershipGroupType(GroupType):
2012-12-06 18:14:57 +01:00
"""allowed normal group (OptionDescription) names
2019-02-23 19:06:23 +01:00
*leadership* means : groups that have the 'leadership' attribute set
2012-12-06 18:14:57 +01:00
"""
pass
2013-04-03 12:20:26 +02:00
class RootGroupType(GroupType):
"""root means this is the root optiondescription of whole config
"""
pass
2019-11-29 12:07:43 +01:00
def addgroup(self, name):
setattr(groups, name, groups.GroupType(name))
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
2019-02-23 19:06:23 +01:00
"""groups.leadership
leadership group is a special optiondescription, all suboptions should
be multi option and all values should have same length, to find
leader's option, the optiondescription's name should be same than de
leader's option"""
2019-02-23 19:06:23 +01:00
groups.leadership = groups.LeadershipGroupType('leadership')
2013-09-07 21:47:17 +02:00
""" groups.root
this group is the root optiondescription of whole config"""
groups.root = groups.RootGroupType('root')
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
forbidden_owners = (owners.default, owners.forced)
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):
2019-02-23 22:52:06 +01:00
def __str__(self): # pragma: no cover
2018-04-07 20:15:19 +02:00
return 'Undefined'
__repr__ = __str__
undefined = Undefined()
2018-10-30 11:57:04 +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"
__slots__ = ('_p_',
'_pp_',
'__weakref__',
'ro_append',
'ro_remove',
'rw_append',
'rw_remove',
'default_properties')
2013-04-03 12:20:26 +02:00
2018-09-07 06:14:52 +02:00
def __init__(self,
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 17:21:09 +02:00
"""
2013-04-03 12:20:26 +02:00
# generic owner
2017-07-13 22:04:06 +02:00
self._p_ = properties
self._pp_ = permissives
self.default_properties = DEFAULT_PROPERTIES
self.ro_append = RO_APPEND
self.ro_remove = RO_REMOVE
self.rw_append = RW_APPEND
self.rw_remove = RW_REMOVE
2013-08-20 09:47:12 +02:00
2018-10-30 11:57:04 +01:00
# ____________________________________________________________
2017-11-20 17:01:36 +01:00
# get properties and permissive methods
2013-03-14 11:31:44 +01:00
2019-12-24 15:24:20 +01:00
async def get_context_properties(self,
2020-01-22 20:46:18 +01:00
connection,
2019-12-24 15:24:20 +01:00
cache):
is_cached, props, validated = cache.getcache(None,
None,
None,
{},
{},
'context_props')
2018-06-25 21:40:16 +02:00
if not is_cached:
2020-01-22 20:46:18 +01:00
props = await self._p_.getproperties(connection,
None,
2019-12-24 15:24:20 +01:00
None,
self.default_properties)
cache.setcache(None,
None,
props,
{},
props,
True)
2015-10-29 09:03:13 +01:00
return props
2013-03-14 11:31:44 +01:00
2019-12-24 15:24:20 +01:00
async def getproperties(self,
option_bag,
apply_requires=True,
2020-01-22 20:46:18 +01:00
uncalculated=False,
help_property=False):
2017-10-22 09:48:08 +02:00
"""
2013-04-03 12:20:26 +02:00
"""
2019-09-01 09:41:53 +02:00
option = option_bag.option
2018-08-01 08:37:58 +02:00
config_bag = option_bag.config_bag
2019-09-01 09:41:53 +02:00
if option.impl_is_symlinkoption():
option = option.impl_getopt()
path = option.impl_getpath()
2018-08-01 08:37:58 +02:00
index = option_bag.index
2020-01-22 20:46:18 +01:00
if apply_requires and not uncalculated and not help_property:
cache = config_bag.context._impl_properties_cache
is_cached, props, validated = cache.getcache(path,
config_bag.expiration_time,
index,
2020-01-22 20:46:18 +01:00
config_bag.properties,
{},
'self_props')
2018-06-25 21:40:16 +02:00
else:
is_cached = False
2017-11-20 17:01:36 +01:00
if not is_cached:
2019-09-01 09:41:53 +02:00
props = set()
2020-01-22 20:46:18 +01:00
# if index, get option's properties (without index) too
p_props = await self._p_.getproperties(config_bag.connection,
path,
2019-12-24 15:24:20 +01:00
None,
option.impl_getproperties())
2019-11-19 18:39:44 +01:00
if index is not None:
p_props = chain(p_props,
2020-01-22 20:46:18 +01:00
await self._p_.getproperties(config_bag.connection,
path,
2019-12-24 15:24:20 +01:00
index,
option.impl_getproperties()))
2019-11-19 18:39:44 +01:00
for prop in p_props:
2019-12-02 10:40:17 +01:00
if uncalculated or isinstance(prop, str):
2020-01-22 20:46:18 +01:00
if not help_property:
props.add(prop)
else:
props.add((prop, prop))
2019-09-01 09:41:53 +02:00
elif apply_requires:
2020-01-22 20:46:18 +01:00
if not help_property:
new_prop = await prop.execute(option_bag,
leadership_must_have_index=True)
else:
new_prop = await prop.help(option_bag,
leadership_must_have_index=True)
if isinstance(new_prop, str):
new_prop = (new_prop, new_prop)
2019-11-19 18:39:44 +01:00
if new_prop is None:
2019-09-01 09:41:53 +02:00
continue
2020-01-22 20:46:18 +01:00
elif (not help_property and not isinstance(new_prop, str)) or \
(help_property and not isinstance(new_prop, tuple)):
2019-10-27 11:09:15 +01:00
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_prop),
2019-09-01 09:41:53 +02:00
option_bag.option.impl_getname(),
prop.function.__name__))
2020-01-22 20:46:18 +01:00
if not option.impl_is_optiondescription() and \
option.impl_is_leader() and \
new_prop not in ALLOWED_LEADER_PROPERTIES:
2019-10-27 11:09:15 +01:00
raise LeadershipError(_('leader cannot have "{}" property').format(new_prop))
props.add(new_prop)
2019-12-24 15:24:20 +01:00
props -= await self.getpermissives(option_bag)
2020-01-22 20:46:18 +01:00
if not uncalculated and apply_requires and not config_bag.is_unrestraint and not help_property:
cache.setcache(path,
index,
props,
props,
config_bag.properties,
True)
2017-11-20 17:01:36 +01:00
return props
2019-12-24 15:24:20 +01:00
async def has_properties_index(self,
option_bag):
2019-11-19 18:39:44 +01:00
option = option_bag.option
if option.impl_is_symlinkoption():
option = option.impl_getopt()
path = option.impl_getpath()
2020-01-22 20:46:18 +01:00
p_props = await self._p_.getproperties(option_bag.config_bag.connection,
path,
2019-12-24 15:24:20 +01:00
None,
option.impl_getproperties())
2019-11-19 18:39:44 +01:00
if option_bag.index is not None:
p_props = chain(p_props,
2020-01-22 20:46:18 +01:00
await self._p_.getproperties(option_bag.config_bag.connection,
path,
2019-12-24 15:24:20 +01:00
option_bag.index,
option.impl_getproperties()))
2019-11-19 18:39:44 +01:00
for prop in p_props:
if not isinstance(prop, str) and prop.has_index(option_bag.option):
2019-09-01 09:41:53 +02:00
return True
return False
2020-01-22 20:46:18 +01:00
async def get_context_permissives(self,
connection):
return await self.getpermissives(None,
connection=connection)
2012-11-19 10:45:03 +01:00
2019-12-24 15:24:20 +01:00
async def getpermissives(self,
2020-01-22 20:46:18 +01:00
option_bag,
connection=None):
2019-11-19 18:39:44 +01:00
if option_bag is None:
path = None
index = None
else:
opt = option_bag.option
if opt.impl_is_symlinkoption():
opt = opt.impl_getopt()
path = opt.impl_getpath()
else:
path = option_bag.path
index = option_bag.index
2020-01-22 20:46:18 +01:00
connection = option_bag.config_bag.connection
permissives = await self._pp_.getpermissives(connection,
path,
None)
2019-11-19 18:39:44 +01:00
if index is not None:
2020-01-22 20:46:18 +01:00
option_permissives = await self._pp_.getpermissives(connection,
path,
index)
2019-12-24 15:24:20 +01:00
permissives = frozenset(option_permissives | permissives)
2019-11-19 18:39:44 +01:00
return permissives
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# set methods
2019-12-24 15:24:20 +01:00
async def set_context_properties(self,
2020-01-22 20:46:18 +01:00
connection,
2019-12-24 15:24:20 +01:00
properties,
context):
2020-01-22 20:46:18 +01:00
await self._p_.setproperties(connection,
None,
2019-12-24 15:24:20 +01:00
None,
properties)
await context.cfgimpl_reset_cache(None)
async def setproperties(self,
path,
properties,
option_bag,
context):
2017-11-20 17:01:36 +01:00
"""save properties for specified path
(never save properties if same has option properties)
"""
opt = option_bag.option
if opt.impl_is_symlinkoption():
2018-04-11 18:32:13 +02:00
raise TypeError(_("can't assign property to the symlinkoption \"{}\""
2017-12-04 20:05:36 +01:00
"").format(opt.impl_get_display_name()))
2019-10-27 11:09:15 +01:00
if not opt.impl_is_optiondescription() and opt.impl_is_leader():
not_allowed_properties = properties - ALLOWED_LEADER_PROPERTIES
if not_allowed_properties:
if len(not_allowed_properties) == 1:
raise LeadershipError(_('leader cannot have "{}" property').format(list(not_allowed_properties)[0]))
else:
raise LeadershipError(_('leader cannot have {} properties').format(display_list(list(not_allowed_properties), add_quote=True)))
if ('force_default_on_freeze' in properties or 'force_metaconfig_on_freeze' in properties) and \
'frozen' not in properties:
raise LeadershipError(_('a leader ({0}) cannot have '
'"force_default_on_freeze" or "force_metaconfig_on_freeze" property without "frozen"'
'').format(opt.impl_get_display_name()))
2020-01-22 20:46:18 +01:00
await self._p_.setproperties(option_bag.config_bag.connection,
path,
2019-12-24 15:24:20 +01:00
option_bag.index,
properties)
2019-02-23 19:06:23 +01:00
# values too because of follower values could have a PropertiesOptionError has value
2019-12-24 15:24:20 +01:00
await context.cfgimpl_reset_cache(option_bag)
option_bag.properties = properties
2017-11-20 17:01:36 +01:00
2019-12-24 15:24:20 +01:00
async def set_context_permissives(self,
2020-01-22 20:46:18 +01:00
connection,
2019-12-24 15:24:20 +01:00
permissives):
await self.setpermissives(None,
2020-01-22 20:46:18 +01:00
permissives,
connection=connection)
2017-11-20 17:01:36 +01:00
2019-12-24 15:24:20 +01:00
async def setpermissives(self,
option_bag,
2020-01-22 20:46:18 +01:00
permissives,
connection=None):
2017-11-20 17:01:36 +01:00
"""
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 not isinstance(permissives, frozenset):
raise TypeError(_('permissive must be a frozenset'))
2018-08-01 08:37:58 +02:00
if option_bag is not None:
opt = option_bag.option
if opt and opt.impl_is_symlinkoption():
raise TypeError(_("can't assign permissive to the symlinkoption \"{}\""
"").format(opt.impl_get_display_name()))
path = option_bag.path
2019-11-19 18:39:44 +01:00
index = option_bag.index
2020-01-22 20:46:18 +01:00
connection = option_bag.config_bag.connection
2018-08-01 08:37:58 +02:00
else:
path = None
2019-11-19 18:39:44 +01:00
index = None
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)))
2020-01-22 20:46:18 +01:00
await self._pp_.setpermissives(connection,
path,
index,
permissives)
2018-08-01 08:37:58 +02:00
if option_bag is not None:
2019-12-24 15:24:20 +01:00
await option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# reset methods
2019-12-24 15:24:20 +01:00
async def reset(self,
option_bag,
2020-01-22 20:46:18 +01:00
config_bag):
2018-08-01 08:37:58 +02:00
if option_bag is None:
opt = None
path = None
2019-11-19 18:39:44 +01:00
index = None
2018-08-01 08:37:58 +02:00
else:
opt = option_bag.option
assert not opt.impl_is_symlinkoption(), _("can't reset properties to "
"the symlinkoption \"{}\""
"").format(opt.impl_get_display_name())
path = option_bag.path
2019-11-19 18:39:44 +01:00
index = option_bag.index
2020-01-22 20:46:18 +01:00
await self._p_.delproperties(config_bag.connection,
path,
index)
await config_bag.context.cfgimpl_reset_cache(option_bag)
2019-12-24 15:24:20 +01:00
async def reset_permissives(self,
option_bag,
2020-01-22 20:46:18 +01:00
config_bag):
if option_bag is None:
opt = None
path = None
2019-11-19 18:39:44 +01:00
index = None
2017-11-20 17:01:36 +01:00
else:
opt = option_bag.option
assert not opt.impl_is_symlinkoption(), _("can't reset permissives to "
"the symlinkoption \"{}\""
"").format(opt.impl_get_display_name())
2019-11-19 18:39:44 +01:00
index = option_bag.index
path = option_bag.path
2020-01-22 20:46:18 +01:00
await self._pp_.delpermissive(config_bag.connection,
path,
index)
await config_bag.context.cfgimpl_reset_cache(option_bag)
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# validate properties
2019-12-24 15:24:20 +01:00
async def calc_raises_properties(self,
option_bag,
2019-12-25 20:44:56 +01:00
apply_requires=True,
uncalculated=False):
if not uncalculated and apply_requires and option_bag.properties_setted:
2019-06-12 08:45:56 +02:00
option_properties = option_bag.properties
else:
2019-12-24 15:24:20 +01:00
option_properties = await self.getproperties(option_bag,
2019-12-25 20:44:56 +01:00
apply_requires=apply_requires,
uncalculated=uncalculated)
2019-07-04 20:43:47 +02:00
return self._calc_raises_properties(option_bag.config_bag.properties,
option_bag.config_bag.permissives,
option_properties)
def _calc_raises_properties(self,
context_properties,
context_permissives,
option_properties):
raises_properties = context_properties - SPECIAL_PROPERTIES
# remove global permissive properties
2020-01-22 20:46:18 +01:00
if raises_properties and 'permissive' in raises_properties:
2019-07-04 20:43:47 +02:00
raises_properties -= context_permissives
2019-06-12 08:45:56 +02:00
properties = option_properties & raises_properties
2020-01-22 20:46:18 +01:00
# at this point it should not remain any property for the option
return properties
2017-11-20 17:01:36 +01:00
2019-12-24 15:24:20 +01:00
async def validate_properties(self,
2020-01-22 20:46:18 +01:00
option_bag,
need_help=True):
config_properties = option_bag.config_bag.properties
if not config_properties or config_properties == frozenset(['cache']):
# if no global property
2018-08-18 07:51:04 +02:00
return
2019-12-24 15:24:20 +01:00
properties = await self.calc_raises_properties(option_bag)
2017-11-20 17:01:36 +01:00
if properties != frozenset():
2020-01-22 20:46:18 +01:00
if need_help:
help_properties = dict(await self.getproperties(option_bag,
help_property=True))
calc_properties = []
for property_ in self._calc_raises_properties(option_bag.config_bag.properties,
option_bag.config_bag.permissives,
set(help_properties.keys())):
calc_properties.append(help_properties[property_])
calc_properties = frozenset(calc_properties)
else:
calc_properties = properties
2018-08-01 08:37:58 +02:00
raise PropertiesOptionError(option_bag,
2017-11-23 16:56:14 +01:00
properties,
2020-01-22 20:46:18 +01:00
self,
help_properties=calc_properties)
2017-11-23 16:56:14 +01:00
2017-11-28 22:42:30 +01:00
def validate_mandatory(self,
value,
2018-08-01 08:37:58 +02:00
option_bag):
2018-08-18 07:51:04 +02:00
if 'mandatory' in option_bag.config_bag.properties:
2018-09-07 06:14:52 +02:00
values = option_bag.config_bag.context.cfgimpl_get_values()
if option_bag.option.impl_is_follower():
force_allow_empty_list = True
else:
force_allow_empty_list = False
if not ('permissive' in option_bag.config_bag.properties and
'mandatory' in option_bag.config_bag.permissives) and \
'mandatory' in option_bag.properties and values.isempty(option_bag.option,
value,
force_allow_empty_list=force_allow_empty_list,
index=option_bag.index):
2019-11-19 18:39:44 +01:00
raise PropertiesOptionError(option_bag,
['mandatory'],
self)
2018-08-17 23:11:25 +02:00
if 'empty' in option_bag.properties and values.isempty(option_bag.option,
2017-12-19 23:11:45 +01:00
value,
force_allow_empty_list=True,
2018-08-01 08:37:58 +02:00
index=option_bag.index):
2018-08-17 23:11:25 +02:00
raise PropertiesOptionError(option_bag,
2019-11-19 18:39:44 +01:00
['empty'],
2018-08-17 23:11:25 +02:00
self)
2017-11-20 17:01:36 +01:00
2017-12-13 22:15:34 +01:00
def validate_frozen(self,
2018-08-01 08:37:58 +02:00
option_bag):
2018-08-18 07:51:04 +02:00
if option_bag.config_bag.properties and \
('everything_frozen' in option_bag.config_bag.properties or
2019-11-19 18:39:44 +01:00
('frozen' in option_bag.config_bag.properties and 'frozen' in option_bag.properties)) and \
2018-08-18 08:06:29 +02:00
not (('permissive' in option_bag.config_bag.properties) and
'frozen' in option_bag.config_bag.permissives):
2018-08-01 08:37:58 +02:00
raise PropertiesOptionError(option_bag,
['frozen'],
self)
2017-12-28 11:47:29 +01:00
return False
2017-11-20 17:01:36 +01:00
#____________________________________________________________
# read only/read write
2019-12-24 15:24:20 +01:00
async def _read(self,
remove,
append,
2020-01-22 20:46:18 +01:00
config_bag):
props = await self._p_.getproperties(config_bag.connection,
None,
2019-12-24 15:24:20 +01:00
None,
self.default_properties)
2017-11-20 17:01:36 +01:00
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:
2020-01-22 20:46:18 +01:00
await self.set_context_properties(config_bag.connection,
frozenset(props),
config_bag.context)
2017-11-20 17:01:36 +01:00
2019-12-24 15:24:20 +01:00
async def read_only(self,
2020-01-22 20:46:18 +01:00
config_bag):
2017-11-20 17:01:36 +01:00
"convenience method to freeze, hide and disable"
2019-12-24 15:24:20 +01:00
await self._read(self.ro_remove,
self.ro_append,
2020-01-22 20:46:18 +01:00
config_bag)
2017-11-20 17:01:36 +01:00
2019-12-24 15:24:20 +01:00
async def read_write(self,
2020-01-22 20:46:18 +01:00
config_bag):
2017-11-20 17:01:36 +01:00
"convenience method to freeze, hide and disable"
2019-12-24 15:24:20 +01:00
await self._read(self.rw_remove,
self.rw_append,
2020-01-22 20:46:18 +01:00
config_bag)