api: permissive => forcepermissive

api: add permissive object
test api: permissive
config: unwrap_from_path check properties for option, not only optiondescription
option: _RegexpOption => RegexpOption
This commit is contained in:
2017-11-03 21:52:13 +01:00
parent 39348a14aa
commit 2e412b64fd
16 changed files with 1073 additions and 986 deletions

View File

@ -254,6 +254,32 @@ class TiramisuAPIProperty(CommonTiramisu):
self.settings.reset(_path=path)
class TiramisuAPIPermissive(CommonTiramisu):
"""manager option's property"""
def __init__(self, config, force_permissive, force_unrestraint):
super(TiramisuAPIPermissive, self).__init__(config, force_permissive, force_unrestraint)
self.config = config
self.force_permissive = force_permissive
self.force_unrestraint = force_unrestraint
if config:
self.settings = config.cfgimpl_get_settings()
def get(self, path):
"""get permissive value for a specified path"""
setting_properties = self.settings.getproperties(None, None, obj=False)
return self.settings.getpermissive(setting_properties, path)
def set(self, path, permissive):
self.settings.setpermissive(permissive, opt=None, path=path)
#def reset(self, path):
# """reset all personalised properties
# """
# self._get_obj_by_path(path)
# self.settings.reset(_path=path)
class TiramisuAPIValue(CommonTiramisu):
"""manager option's value"""
@ -348,7 +374,7 @@ class TiramisuAPI(object):
return self.registers[subfunc](self.config,
self.force_permissive,
self.force_unrestraint)
elif subfunc == 'permissive':
elif subfunc == 'forcepermissive':
return TiramisuAPI(self.config, force_permissive=True, force_unrestraint=self.force_unrestraint)
elif subfunc == 'unrestraint':
return TiramisuAPI(self.config, force_permissive=self.force_permissive, force_unrestraint=True)
@ -358,7 +384,7 @@ class TiramisuAPI(object):
raise APIError(_('please specify a valid sub function'))
def _help(self):
txt = ['[permissive]']
txt = ['[forcepermissive]']
for module_name, module in self.registers.items():
module_doc = getdoc(module)
txt.append(self.tmpl_help.format(self.icon, module_name, module_doc))

View File

@ -22,7 +22,7 @@
import weakref
import sys
from time import time
from itertools import chain
from copy import copy
from .error import PropertiesOptionError, ConfigError, ConflictError
@ -76,69 +76,33 @@ class SubConfig(object):
only=('values', 'properties', 'permissives', 'settings'),
opt=None,
path=None,
orig_opts=None):
resetted_opts=None):
"""reset all settings in cache
:param only_expired: if True reset only expired cached values
:type only_expired: boolean
"""
def reset_one_option_cache(opt, path):
if opt.__class__.__name__ == 'DynOptionDescription':
# this option is a DynOptionDescription
descr = context.cfgimpl_get_description()
spath = path.split('.')
subpath = '.'.join(spath[:-1])
dynopt = getattr(descr, subpath)._getattr(spath[-1], context=context,
dyn=False)
for suffix in dynopt._impl_get_suffixes(context):
path = subpath + '.' + spath[-1] + suffix
if 'values' in only:
values._p_.delcache(path)
if 'settings' in only or 'properties' in only:
settings._p_.delcache(path)
if 'settings' in only or 'permissives' in only:
settings._pp_.delcache(path)
elif not isinstance(opt, DynSymLinkOption) and opt._is_subdyn():
# this option is an instance of DynOptionDescription
descr = context.cfgimpl_get_description()
spath = path.split('.')
try:
subpath = '.'.join(spath[:-2])
dynsubopt = getattr(descr, subpath)
spath1 = spath[-2]
spath2 = spath[-1]
spath3 = None
except AttributeError:
if len(spath) == 2:
subpath = '.'.join(spath)
spath1 = spath[-2]
spath2 = spath[-1]
spath3 = None
dynsubopt = descr
else:
subpath = '.'.join(spath[:-3])
spath1 = spath[-3]
spath2 = spath[-2]
spath3 = spath[-1]
dynsubopt = getattr(descr, subpath)
dynopt = dynsubopt._getattr(spath1, context=context, dyn=False)
for suffix in dynopt._impl_get_suffixes(context):
path = subpath + '.' + spath1 + suffix + '.' + spath2 + suffix
if spath3:
path += '.' + spath3 + suffix
if 'values' in only:
values._p_.delcache(path)
if 'settings' in only or 'properties' in only:
settings._p_.delcache(path)
if 'settings' in only or 'permissives' in only:
settings._pp_.delcache(path)
def reset_one_option_cache(opt, resetted_opts):
if 'values' in only:
tresetted_opts = copy(resetted_opts)
opt.reset_cache(opt, values, 'values', tresetted_opts)
if 'settings' in only:
tresetted_opts = copy(resetted_opts)
opt.reset_cache(opt, settings, 'settings', tresetted_opts)
else:
if 'values' in only:
values._p_.delcache(path)
if 'settings' in only or 'permissives' in only:
settings._p_.delcache(path)
if 'settings' in only or 'permissives' in only:
settings._pp_.delcache(path)
if 'properties' in only:
tresetted_opts = copy(resetted_opts)
opt.reset_cache(opt, settings, 'properties', tresetted_opts)
if 'permissives' in only:
tresetted_opts = copy(resetted_opts)
opt.reset_cache(opt, settings, 'permissives', tresetted_opts)
resetted_opts |= tresetted_opts
for option in opt._get_dependencies(self):
if option in resetted_opts:
continue
reset_one_option_cache(option, resetted_opts)
def reset_expired_cache():
# reset cache for expired cache value ony
@ -156,9 +120,14 @@ class SubConfig(object):
if 'settings' in only:
settings._p_.reset_all_cache()
settings._pp_.reset_all_cache()
else:
if 'properties' in only:
settings._p_.reset_all_cache()
if 'permissives' in only:
settings._pp_.reset_all_cache()
if orig_opts is None:
orig_opts = set()
if resetted_opts is None:
resetted_opts = set()
context = self._cfgimpl_get_context()
if 'values' in only:
@ -167,26 +136,8 @@ class SubConfig(object):
settings = context.cfgimpl_get_settings()
if not None in (opt, path):
reset_one_option_cache(opt, path)
# remove cache for option which has dependencies with this option
#if not isinstance(opt, OptionDescription) and not isinstance(opt, SynDynOptionDescription) and \
# opt.impl_is_master_slaves('slave'):
# slaves = opt.impl_get_master_slaves().getslaves(opt)
#else:
slaves = []
for option in chain(opt._get_dependencies(self), slaves):
if option in orig_opts:
continue
if 'values' in only:
option.reset_cache(opt, values, 'values', orig_opts)
if 'settings' in only:
option.reset_cache(opt, settings, 'settings', orig_opts)
else:
if 'properties' in only:
option.reset_cache(opt, settings, 'properties', orig_opts)
if 'permissives' in only:
option.reset_cache(opt, settings, 'permissives', orig_opts)
if opt not in resetted_opts:
reset_one_option_cache(opt, resetted_opts)
elif only_expired:
reset_expired_cache()
@ -445,6 +396,8 @@ class SubConfig(object):
return props
else:
raise props
if returns_option is True:
return option
return SubConfig(option, self._impl_context, subpath)
else:
if validate:
@ -737,23 +690,28 @@ class _CommonConfig(SubConfig):
force_permissive=force_permissive)
def unwrap_from_path(self, path, force_permissive=False, index=None,
validate_properties=True, validate=True):
validate_properties=True, validate=True,
_setting_properties=undefined):
"""convenience method to extract and Option() object from the Config()
and it is **fast**: finds the option directly in the appropriate
namespace
:returns: Option()
"""
context = self._cfgimpl_get_context()
if _setting_properties is undefined:
context = self._cfgimpl_get_context()
_setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=True)
if '.' in path:
self, path = self.cfgimpl_get_home_by_path(path,
validate_properties=validate_properties,
force_permissive=force_permissive)
force_permissive=force_permissive,
_setting_properties=_setting_properties)
return self.getattr(path,
validate_properties=validate_properties,
validate=validate,
force_permissive=force_permissive,
index=index,
_setting_properties=_setting_properties,
returns_option=True)
def cfgimpl_get_path(self, dyn=True):
@ -889,13 +847,19 @@ class GroupConfig(_CommonConfig):
only=('values', 'settings'),
opt=None,
path=None,
orig_opts=set()):
resetted_opts=set()):
if isinstance(self, MetaConfig):
super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired, only=only,
opt=opt, path=path, orig_opts=orig_opts)
super(GroupConfig, self).cfgimpl_reset_cache(only_expired=only_expired,
only=only,
opt=opt,
path=path,
resetted_opts=copy(resetted_opts))
for child in self._impl_children:
child.cfgimpl_reset_cache(only_expired=only_expired, only=only, opt=opt, path=path,
orig_opts=orig_opts)
child.cfgimpl_reset_cache(only_expired=only_expired,
only=only,
opt=opt,
path=path,
resetted_opts=copy(resetted_opts))
def set_value(self, path, value, _commit=True):
"""Setattr not in current GroupConfig, but in each children
@ -964,7 +928,8 @@ class GroupConfig(_CommonConfig):
def getattr(self, name, force_permissive=False, validate=True,
_setting_properties=undefined, _self_properties=undefined, index=None,
returns_raise=False):
returns_raise=False, returns_option=False,
validate_properties=True):
for child in self._impl_children:
if name == child._impl_name:
return child
@ -973,7 +938,9 @@ class GroupConfig(_CommonConfig):
_self_properties=_self_properties,
index=index,
_setting_properties=_setting_properties,
returns_raise=returns_raise)
returns_raise=returns_raise,
returns_option=False,
validate_properties=validate_properties)
class MetaConfig(GroupConfig):

View File

@ -404,15 +404,16 @@ class BaseOption(Base):
name = name.encode('utf8')
return name
def reset_cache(self, opt, obj, type_, orig_opts):
context = obj._getcontext()
path = self.impl_getpath(context)
obj._p_.delcache(path)
orig_opts.add(opt)
context.cfgimpl_reset_cache(only=(type_,),
opt=self,
path=path,
orig_opts=orig_opts)
def reset_cache(self, opt, obj, type_, resetted_opts):
if opt in resetted_opts:
return
if not type_ == 'values' or not opt.impl_is_optiondescription():
path = opt.impl_getpath(obj._getcontext())
if type_ != 'permissives':
obj._p_.delcache(path)
if type_ in ['settings', 'permissives']:
obj._pp_.delcache(path)
resetted_opts.add(opt)
def _is_symlinkoption(self):
return False

View File

@ -21,10 +21,10 @@
import re
from ..i18n import _
from .option import _RegexpOption
from .option import RegexpOption
class EmailOption(_RegexpOption):
class EmailOption(RegexpOption):
__slots__ = tuple()
#https://www.w3.org/TR/html-markup/input.email.html#input.email.attrs.value.single.
_regexp = re.compile(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")

View File

@ -21,10 +21,10 @@
import re
from ..i18n import _
from .option import _RegexpOption
from .option import RegexpOption
class FilenameOption(_RegexpOption):
class FilenameOption(RegexpOption):
__slots__ = tuple()
_regexp = re.compile(r"^[a-zA-Z0-9\-\._~/+]+$")
_display_name = _('file name')

View File

@ -503,7 +503,7 @@ class Option(OnlyOption):
all_cons_opts = tuple([self] + list(other_opts))
unknown_params = set(params.keys()) - set(['warnings_only', 'transitive'])
if unknown_params != set():
raise ValueError(_('unknow parameter {0} in consistency').format(unknown_params))
raise ValueError(_('unknown parameter {0} in consistency').format(unknown_params))
self._add_consistency(func, all_cons_opts, params)
#validate default value when add consistency
err = self.impl_validate(self.impl_getdefault())
@ -660,7 +660,7 @@ class Option(OnlyOption):
return hasattr(self, '_consistencies')
class _RegexpOption(Option):
class RegexpOption(Option):
__slots__ = tuple()
def _validate(self, value, context=undefined, current_opt=undefined):
@ -670,3 +670,7 @@ class _RegexpOption(Option):
match = self._regexp.search(value)
if not match:
return ValueError()
#FIXME compatibility
_RegexpOption = RegexpOption

View File

@ -358,11 +358,10 @@ class OptionDescriptionWalk(CacheOptionDescription):
for child in self._impl_st_getchildren(context):
cname = child.impl_getname()
if dyn and child.impl_is_dynoptiondescription():
path = cname
for value in child._impl_get_suffixes(context):
yield SynDynOptionDescription(child,
cname + value,
path + value, value)
value)
else:
yield child
@ -383,17 +382,16 @@ class OptionDescriptionWalk(CacheOptionDescription):
for child in self._impl_st_getchildren(context, only_dyn=True):
cname = child.impl_getname()
if name.startswith(cname):
path = cname
for value in child._impl_get_suffixes(context):
if name == cname + value:
return SynDynOptionDescription(child, name, path + value, value)
return SynDynOptionDescription(child, name, value)
return ret
def _impl_get_dynchild(self, child, suffix):
name = child.impl_getname() + suffix
path = self.impl_getname() + suffix + '.' + name
if isinstance(child, OptionDescription):
return SynDynOptionDescription(child, name, path, suffix)
return SynDynOptionDescription(child, name, suffix)
else:
return child._impl_to_dyn(name, path)
@ -524,12 +522,11 @@ class DynOptionDescription(OptionDescription):
class SynDynOptionDescription(object):
__slots__ = ('_opt', '_name', '_path', '_suffix')
__slots__ = ('_opt', '_name', '_suffix')
def __init__(self, opt, name, path, suffix):
def __init__(self, opt, name, suffix):
self._opt = opt
self._name = name
self._path = path
self._suffix = suffix
def __getattr__(self, name, context=undefined):
@ -549,7 +546,10 @@ class SynDynOptionDescription(object):
return self._impl_getchildren()
def impl_getpath(self, context):
return self._path
path = self._impl_getopt().impl_getpath(context).split('.')
path[-1] += self._suffix
path.append(self._name)
return '.'.join(path)
def impl_getpaths(self, include_groups=False, _currpath=None):
return _impl_getpaths(self, include_groups, _currpath)
@ -825,12 +825,13 @@ class MasterSlaves(OptionDescription):
" which has {1} as master").format(
name, opt.impl_getname()))
def reset_cache(self, opt, values, type_, orig_opts):
path = self.getmaster(opt).impl_getpath(values._getcontext())
values._p_.delcache(path)
for slave in self.getslaves(opt):
slave_path = slave.impl_getpath(values._getcontext())
values._p_.delcache(slave_path)
def reset_cache(self, opt, obj, type_, resetted_opts):
context = obj._getcontext()
#FIXME pb avec dyn, devrait etre une option
mopt = self.getmaster(None)
mopt.reset_cache(mopt, obj, type_, resetted_opts)
for slave in self.getslaves(mopt):
slave.reset_cache(slave, obj, type_, resetted_opts)
def _getattr(self, name, suffix=undefined, context=undefined, dyn=True):
return super(MasterSlaves, self)._getattr(name, suffix, context, dyn)

View File

@ -375,7 +375,7 @@ class Settings(object):
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=opt, path=_path, only=('settings',))
self._getcontext().cfgimpl_reset_cache(opt=opt, path=_path, only=('settings', 'values'))
def _getproperties(self, opt=None, path=None,
setting_properties=undefined, read_write=True,
@ -394,12 +394,12 @@ class Settings(object):
props = self._p_.getproperties(path, default_properties)
else:
props = meta.cfgimpl_get_settings()._getproperties()
if 'cache' in props:
if 'expire' in props:
ntime = ntime + expires_time
else:
ntime = None
self._p_.setcache(path, props, ntime, None)
if 'cache' in props:
if 'expire' in props:
ntime = ntime + expires_time
else:
ntime = None
self._p_.setcache(path, props, ntime, None)
else:
if path is None: # pragma: optional cover
raise ValueError(_('if opt is not None, path should not be'
@ -468,7 +468,8 @@ class Settings(object):
raise ConfigError(_('cannot add those properties: {0}').format(
' '.join(forbidden_properties)))
self._p_.setproperties(path, properties)
self._getcontext().cfgimpl_reset_cache(opt=opt, path=path)
#values too because of slave values could have a PropertiesOptionError has value
self._getcontext().cfgimpl_reset_cache(opt=opt, path=path, only=('values', 'properties',))
def getpermissive(self, setting_properties, path=None):
if 'cache' in setting_properties and 'expire' in setting_properties:
@ -595,7 +596,7 @@ class Settings(object):
raise TypeError(_('permissive must be a tuple'))
self._pp_.setpermissive(path, permissive)
setting_properties = self._getproperties(read_write=False)
self._getcontext().cfgimpl_reset_cache(opt=opt, path=path, only=('values',))
self._getcontext().cfgimpl_reset_cache(opt=opt, path=path, only=('properties', 'values'))
if 'cache' in setting_properties:
if 'expire' in setting_properties:
ntime = int(time()) + expires_time

View File

@ -87,7 +87,7 @@ class Values(object):
callback=callback,
callback_params=callback_params,
index=index, validate=validate)
_orig_context.cfgimpl_reset_cache(opt=opt, path=path, only=('values',))
_orig_context.cfgimpl_reset_cache(opt=opt, path=path, only=('values', 'properties'))
if isinstance(value, list) and index is not None:
#if return a list and index is set, return value only if
#it's a submulti without submulti_index and without list of list
@ -295,7 +295,8 @@ class Values(object):
if isinstance(val, Exception):
return val
# cache doesn't work with SubMulti yet
if index is None and not isinstance(val, SubMulti) and 'cache' in setting_properties and \
if not from_masterslave and index is None and not isinstance(val, SubMulti) and \
'cache' in setting_properties and \
validate and validate_properties and force_permissive is False \
and trusted_cached_properties is True and _orig_context is undefined:
if 'expire' in setting_properties: