New Postgres storage

This commit is contained in:
2020-01-22 20:46:18 +01:00
parent 746fa0134f
commit 3bef45c9db
85 changed files with 10814 additions and 9262 deletions

View File

@ -1,4 +1,4 @@
# Copyright (C) 2012-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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
@ -25,6 +25,7 @@ from .error import APIError, ConfigError, LeadershipError, PropertiesOptionError
from .i18n import _
from .setting import ConfigBag, OptionBag, owners, groups, Undefined, undefined, \
FORBIDDEN_SET_PROPERTIES, SPECIAL_PROPERTIES, EXPIRATION_TIME
from .storage import default_storage
from .config import KernelConfig, SubConfig, KernelGroupConfig, KernelMetaConfig, KernelMixConfig
from .option import RegexpOption, OptionDescription
from .todict import TiramisuDict
@ -84,7 +85,8 @@ class CommonTiramisu(TiramisuHelp):
_allow_optiondescription = True
_validate_properties = True
async def _get_option(self) -> Any:
async def _get_option(self,
connection) -> Any:
if not self._subconfig:
config_bag = self._option_bag.config_bag
try:
@ -103,6 +105,7 @@ class CommonTiramisu(TiramisuHelp):
config_bag,
self._subconfig.cfgimpl_get_path())
self._option_bag.option = option
self._option_bag.config_bag.connection = connection
# Calculate option's properties
settings = config_bag.context.cfgimpl_get_settings()
@ -141,6 +144,18 @@ class CommonTiramisuOption(CommonTiramisu):
raise APIError(_('unknown method "{}" in "{}"').format(name, self.__class__.__name__))
def option_and_connection(func):
async def wrapped(self, *args, **kwargs):
config_bag = self._option_bag.config_bag
async with config_bag.context.getconnection() as connection:
config_bag.connection = connection
option = await self._get_option(connection)
ret = await func(self, *args, **kwargs)
del config_bag.connection
return ret
return wrapped
class _TiramisuOptionOptionDescription(CommonTiramisuOption):
"""Manage option"""
_allow_optiondescription = True
@ -152,60 +167,62 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
super().__init__(option_bag)
self._config = option_bag.config_bag.context
@option_and_connection
async def get(self):
"""Get Tiramisu option"""
return await self._get_option()
return self._option_bag.option
@option_and_connection
async def type(self):
option = await self._get_option()
return option.impl_get_group_type()
return self._option_bag.option.impl_get_group_type()
@option_and_connection
async def isleadership(self):
"""Test if option is a leader or a follower"""
option = await self._get_option()
return option.impl_is_leadership()
return self._option_bag.option.impl_is_leadership()
@option_and_connection
async def doc(self):
"""Get option document"""
option = await self._get_option()
return option.impl_get_display_name()
return self._option_bag.option.impl_get_display_name()
@option_and_connection
async def description(self):
"""Get option description"""
option = await self._get_option()
return option.impl_get_information('doc', None)
return self._option_bag.option.impl_get_information('doc', None)
@option_and_connection
async def name(self,
follow_symlink: bool=False) -> str:
"""Get option name"""
option = await self._get_option()
if not follow_symlink or \
await self.isoptiondescription() or \
not await self.issymlinkoption():
return option.impl_getname()
self._option_bag.option.impl_is_optiondescription() or \
not self._option_bag.option.impl_is_symlinkoption():
return self._option_bag.option.impl_getname()
else:
return option.impl_getopt().impl_getname()
return self._option_bag.option.impl_getopt().impl_getname()
@option_and_connection
async def path(self) -> str:
"""Get option path"""
return self._option_bag.path
@option_and_connection
async def has_dependency(self, self_is_dep=True):
"""Test if option has dependency"""
option = await self._get_option()
return option.impl_has_dependency(self_is_dep)
return self._option_bag.option.impl_has_dependency(self_is_dep)
@option_and_connection
async def isoptiondescription(self):
"""Test if option is an optiondescription"""
option = await self._get_option()
return option.impl_is_optiondescription()
return self._option_bag.option.impl_is_optiondescription()
@option_and_connection
async def properties(self,
only_raises=False,
uncalculated=False):
"""Get properties for an option"""
settings = self._option_bag.config_bag.context.cfgimpl_get_settings()
option = await self._get_option()
if uncalculated:
return await settings.getproperties(self._option_bag,
uncalculated=True)
@ -229,38 +246,39 @@ class _TiramisuOptionOptionDescription(CommonTiramisuOption):
class TiramisuOptionOption(_TiramisuOptionOptionDescription):
"""Manage option"""
@option_and_connection
async def ismulti(self):
"""Test if option could have multi value"""
option = await self._get_option()
return option.impl_is_multi()
return self._option_bag.option.impl_is_multi()
@option_and_connection
async def issubmulti(self):
"""Test if option could have submulti value"""
option = await self._get_option()
return option.impl_is_submulti()
return self._option_bag.option.impl_is_submulti()
@option_and_connection
async def isleader(self):
"""Test if option is a leader"""
option = await self._get_option()
return option.impl_is_leader()
return self._option_bag.option.impl_is_leader()
@option_and_connection
async def isfollower(self):
"""Test if option is a follower"""
option = await self._get_option()
return option.impl_is_follower()
return self._option_bag.option.impl_is_follower()
@option_and_connection
async def issymlinkoption(self) -> bool:
option = await self._get_option()
return option.impl_is_symlinkoption()
return self._option_bag.option.impl_is_symlinkoption()
@option_and_connection
async def default(self):
"""Get default value for an option (not for optiondescription)"""
option = await self._get_option()
return option.impl_getdefault()
return self._option_bag.option.impl_getdefault()
@option_and_connection
async def defaultmulti(self):
"""Get default value when added a value for a multi option (not for optiondescription)"""
option = await self._get_option()
option = self._option_bag.option
ret = option.impl_getdefault_multi()
if ret is None and option.impl_is_multi() and option.impl_has_callback() and not self.isfollower():
callback, callback_params = option.impl_get_callback()
@ -272,22 +290,23 @@ class TiramisuOptionOption(_TiramisuOptionOptionDescription):
ret = value
return ret
@option_and_connection
async def callbacks(self):
"""Get callbacks for an option (not for optiondescription)"""
option = await self._get_option()
return option.impl_get_callback()
return self._option_bag.option.impl_get_callback()
@option_and_connection
async def validator(self):
"""Get validator for an option (not for optiondescription)"""
option = await self._get_option()
return option.impl_get_validator()
return self._option_bag.option.impl_get_validator()
@option_and_connection
async def type(self):
option = await self._get_option()
return option.get_type()
return self._option_bag.option.get_type()
@option_and_connection
async def pattern(self) -> str:
option = await self._get_option()
option = self._option_bag.option
type = option.get_type()
if isinstance(option, RegexpOption):
return option._regexp.pattern
@ -313,19 +332,19 @@ class TiramisuOptionOwner(CommonTiramisuOption):
# for help()
self._values = self._option_bag.config_bag.context.cfgimpl_get_values()
@option_and_connection
async def get(self):
"""Get owner for a specified option"""
await self._get_option()
return await self._values.getowner(self._option_bag)
@option_and_connection
async def isdefault(self):
"""Is option has defaut value"""
await self._get_option()
return await self._values.is_default_owner(self._option_bag)
@option_and_connection
async def set(self, owner):
"""Get owner for a specified option"""
await self._get_option()
try:
obj_owner = getattr(owners, owner)
except AttributeError:
@ -347,47 +366,50 @@ class TiramisuOptionProperty(CommonTiramisuOption):
if option_bag and option_bag.config_bag:
self._settings = option_bag.config_bag.context.cfgimpl_get_settings()
@option_and_connection
async def get(self,
only_raises=False,
uncalculated=False):
"""Get properties for an option"""
await self._get_option()
if not only_raises:
return self._option_bag.properties
# do not check cache properties/permissives which are not save (unrestraint, ...)
return await self._settings.calc_raises_properties(self._option_bag,
uncalculated=uncalculated)
ret = await self._settings.calc_raises_properties(self._option_bag,
uncalculated=uncalculated)
return ret
@option_and_connection
async def add(self, prop):
"""Add new property for an option"""
option = await self._get_option()
if prop in FORBIDDEN_SET_PROPERTIES:
raise ConfigError(_('cannot add this property: "{0}"').format(
' '.join(prop)))
props = await self._settings._p_.getproperties(self._option_bag.path,
props = await self._settings._p_.getproperties(self._option_bag.config_bag.connection,
self._option_bag.path,
self._option_bag.index,
option.impl_getproperties())
self._option_bag.option.impl_getproperties())
await self._settings.setproperties(self._option_bag.path,
props | {prop},
self._option_bag,
self._option_bag.config_bag.context)
@option_and_connection
async def pop(self, prop):
"""Remove new property for an option"""
option = await self._get_option()
props = await self._settings._p_.getproperties(self._option_bag.path,
props = await self._settings._p_.getproperties(self._option_bag.config_bag.connection,
self._option_bag.path,
self._option_bag.index,
option.impl_getproperties())
self._option_bag.option.impl_getproperties())
await self._settings.setproperties(self._option_bag.path,
props - {prop},
self._option_bag,
self._option_bag.config_bag.context)
@option_and_connection
async def reset(self):
"""Reset all personalised properties"""
await self._get_option()
await self._settings.reset(self._option_bag,
self._option_bag.config_bag.context)
self._option_bag.config_bag)
class TiramisuOptionPermissive(CommonTiramisuOption):
@ -401,22 +423,22 @@ class TiramisuOptionPermissive(CommonTiramisuOption):
if option_bag and option_bag.config_bag:
self._settings = option_bag.config_bag.context.cfgimpl_get_settings()
@option_and_connection
async def get(self):
"""Get permissives value"""
await self._get_option()
return await self._settings.getpermissives(self._option_bag)
@option_and_connection
async def set(self, permissives):
"""Set permissives value"""
await self._get_option()
await self._settings.setpermissives(self._option_bag,
permissives=permissives)
@option_and_connection
async def reset(self):
"""Reset all personalised permissive"""
await self._get_option()
await self._settings.reset_permissives(self._option_bag,
self._option_bag.config_bag.context)
self._option_bag.config_bag)
class TiramisuOptionInformation(CommonTiramisuOption):
@ -428,45 +450,53 @@ class TiramisuOptionInformation(CommonTiramisuOption):
option_bag: OptionBag) -> None:
super().__init__(option_bag)
@option_and_connection
async def get(self, key, default=undefined):
"""Get information"""
path = self._option_bag.path
values = self._option_bag.config_bag.context.cfgimpl_get_values()
try:
return await values.get_information(key,
return await values.get_information(self._option_bag.config_bag.connection,
key,
path=path)
except ValueError:
option = await self._get_option()
return option.impl_get_information(key, default)
return self._option_bag.option.impl_get_information(key, default)
@option_and_connection
async def set(self, key, value):
"""Set information"""
path = self._option_bag.path
values = self._option_bag.config_bag.context.cfgimpl_get_values()
await values.set_information(key, value, path=path)
await values.set_information(self._option_bag.config_bag.connection,
key,
value,
path=path)
@option_and_connection
async def reset(self,
key):
"""Remove information"""
path = self._option_bag.path
values = self._option_bag.config_bag.context.cfgimpl_get_values()
await values.del_information(key,
await values.del_information(self._option_bag.config_bag.connection,
key,
path=path)
@option_and_connection
async def list(self):
"""List information's keys"""
await self._get_option()
path = self._option_bag.path
values = self._option_bag.config_bag.context.cfgimpl_get_values()
return await values.list_information(path)
async def len(self):
"""Length of leadership"""
option = await self._get_option()
# for example if index is None
if '_length' not in vars(self):
self._length = self._subconfig.cfgimpl_get_length()
return self._length
return await values.list_information(self._option_bag.config_bag.connection,
path)
#
# async def len(self):
# """Length of leadership"""
# option = await self._get_option()
# # for example if index is None
# if '_length' not in vars(self):
# self._length = self._subconfig.cfgimpl_get_length()
# return self._length
def option_type(typ):
@ -478,28 +508,41 @@ def option_type(typ):
def wrapper(func):
@wraps(func)
async def wrapped(*args, **kwargs):
for typ in types:
if typ == 'group':
if args[0]._option_bag.config_bag.context.impl_type == 'group':
return await func(*args, **kwargs, is_group=True)
else:
option = await args[0]._get_option()
if typ == 'option':
if option.impl_is_optiondescription():
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'optiondescription':
if not option.impl_is_optiondescription():
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'leader':
if not option.impl_is_leader():
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'follower':
if not option.impl_is_follower() and not option.impl_is_leader():
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'choice':
if not option.get_type() == 'choice':
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
return await func(*args, **kwargs)
config_bag = args[0]._option_bag.config_bag
async with config_bag.context.getconnection() as connection:
for typ in types:
if typ == 'group':
if args[0]._option_bag.config_bag.context.impl_type == 'group':
config_bag.connection = connection
ret = await func(*args, **kwargs, is_group=True)
del config_bag.connection
return ret
else:
config_bag.connection = connection
option = await args[0]._get_option(connection)
if typ == 'option':
if option.impl_is_optiondescription():
del config_bag.connection
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'optiondescription':
if not option.impl_is_optiondescription():
del config_bag.connection
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'leader':
if not option.impl_is_leader():
del config_bag.connection
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'follower':
if not option.impl_is_follower() and not option.impl_is_leader():
del config_bag.connection
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
elif typ == 'choice':
if not option.get_type() == 'choice':
del config_bag.connection
raise APIError(_('please specify a valid sub function ({})').format(func.__name__))
ret = await func(*args, **kwargs)
del config_bag.connection
return ret
return wrapped
return wrapper
@ -559,7 +602,8 @@ class TiramisuOptionValue(CommonTiramisuOption):
is_group: bool=False):
"""Reset value for an option"""
if is_group:
await self._option_bag.config_bag.context.reset(self._option_bag.path)
await self._option_bag.config_bag.context.reset(self._option_bag.config_bag.connection,
self._option_bag.path)
else:
if self._option_bag.option.impl_is_follower() and self._option_bag.index is None:
raise APIError('index must be set with a follower option')
@ -568,7 +612,7 @@ class TiramisuOptionValue(CommonTiramisuOption):
@option_type('option')
async def default(self):
"""Get default value (default of option or calculated value)"""
option = await self._get_option()
option = self._option_bag.option
values = self._option_bag.config_bag.context.cfgimpl_get_values()
if option.impl_is_follower() and self._option_bag.index is None:
value = []
@ -601,8 +645,7 @@ class TiramisuOptionValue(CommonTiramisuOption):
@option_type('choice')
async def list(self):
"""All values available for a ChoiceOption"""
option = await self._get_option()
return await option.impl_get_values(self._option_bag)
return await self._option_bag.option.impl_get_values(self._option_bag)
@option_type('leader')
async def pop(self, index):
@ -618,7 +661,6 @@ class TiramisuOptionValue(CommonTiramisuOption):
@option_type('follower')
async def len(self):
"""Length of follower option"""
option = await self._get_option()
# for example if index is None
if '_length' not in vars(self):
self._length = await self._subconfig.cfgimpl_get_length_leadership(self._option_bag)
@ -645,22 +687,30 @@ class TiramisuConfig(TiramisuHelp):
self._orig_config_bags = orig_config_bags
async def _return_config(self,
config):
config,
storage):
if isinstance(config, KernelConfig):
return await Config(config)
return await Config(config,
storage=storage)
if isinstance(config, KernelMetaConfig):
return await MetaConfig(config)
return await MetaConfig(config,
storage=storage)
if isinstance(config, KernelMixConfig):
return await MixConfig([], config)
return await MixConfig([],
config,
storage=storage)
if isinstance(config, KernelGroupConfig):
return await GroupConfig(config)
return await GroupConfig(config,
storage=storage)
raise Exception(_('unknown config type {}').format(type(config)))
async def _reset_config_properties(self):
async def _reset_config_properties(self,
connection):
config = self._config_bag.context
settings = config.cfgimpl_get_settings()
properties = await settings.get_context_properties(config._impl_properties_cache)
permissives = await settings.get_context_permissives()
properties = await settings.get_context_properties(connection,
config._impl_properties_cache)
permissives = await settings.get_context_permissives(connection)
self._config_bag.properties = properties
self._config_bag.permissives = permissives
if self._orig_config_bags:
@ -700,7 +750,7 @@ class TiramisuOption(CommonTiramisu, TiramisuConfig):
"""find an option by name (only for optiondescription)"""
if not first:
ret = []
option = await self._get_option()
option = self._option_bag.option
config_bag = self._option_bag.config_bag
oname = option.impl_getname()
path = self._subconfig._get_subpath(oname)
@ -725,8 +775,7 @@ class TiramisuOption(CommonTiramisu, TiramisuConfig):
@option_type('optiondescription')
async def group_type(self):
"""Get type for an optiondescription (only for optiondescription)"""
option = await self._get_option()
return option.impl_get_group_type()
return self._option_bag.option.impl_get_group_type()
async def _filter(self,
opt,
@ -756,7 +805,7 @@ class TiramisuOption(CommonTiramisu, TiramisuConfig):
if config_bag.properties and 'warnings' in config_bag.properties:
config_bag = config_bag.copy()
config_bag.remove_warnings()
option = await self._get_option()
option = self._option_bag.option
option_bag = OptionBag()
option_bag.set_option(option,
None,
@ -784,9 +833,10 @@ class TiramisuOption(CommonTiramisu, TiramisuConfig):
async def _load_dict(self,
clearable: str="all",
remotable: str="minimum"):
option = await self._get_option()
root = option.impl_getpath()
self._tiramisu_dict = TiramisuDict(await self._return_config(self._option_bag.config_bag.context),
root = self._option_bag.option.impl_getpath()
config = self._option_bag.config_bag.context
self._tiramisu_dict = TiramisuDict(await self._return_config(config,
config._storage),
root=root,
clearable=clearable,
remotable=remotable)
@ -811,27 +861,48 @@ class TiramisuOption(CommonTiramisu, TiramisuConfig):
return await self._tiramisu_dict.set_updates(body)
def connection(func):
async def wrapped(self, *args, **kwargs):
config_bag = self._config_bag
async with config_bag.context.getconnection() as connection:
config_bag.connection = connection
ret = await func(self, *args, **kwargs)
del config_bag.connection
return ret
return wrapped
class TiramisuContextInformation(TiramisuConfig):
"""Manage config informations"""
@connection
async def get(self, name, default=undefined):
"""Get an information"""
return await self._config_bag.context.impl_get_information(name, default)
return await self._config_bag.context.impl_get_information(self._config_bag.connection,
name,
default)
@connection
async def set(self, name, value):
"""Set an information"""
await self._config_bag.context.impl_set_information(name, value)
await self._config_bag.context.impl_set_information(self._config_bag.connection,
name,
value)
@connection
async def reset(self, name):
"""Remove an information"""
await self._config_bag.context.impl_del_information(name)
await self._config_bag.context.impl_del_information(self._config_bag.connection,
name)
@connection
async def list(self):
"""List information's keys"""
return await self._config_bag.context.impl_list_information()
return await self._config_bag.context.impl_list_information(self._config_bag.connection)
class TiramisuContextValue(TiramisuConfig):
"""Manage config value"""
@connection
async def mandatory(self):
"""Return path of options with mandatory property without any value"""
options = []
@ -840,6 +911,7 @@ class TiramisuContextValue(TiramisuConfig):
return options
# FIXME should be only for group/meta
@connection
async def set(self,
path: str,
value,
@ -865,6 +937,7 @@ class TiramisuContextValue(TiramisuConfig):
**kwargs)
# FIXME should be only for group/meta
@connection
async def reset(self,
path: str,
only_children: bool=False):
@ -873,6 +946,7 @@ class TiramisuContextValue(TiramisuConfig):
only_children,
self._config_bag)
@connection
async def dict(self,
flatten=False,
withwarning: bool=False,
@ -888,10 +962,11 @@ class TiramisuContextValue(TiramisuConfig):
fullpath=fullpath,
leader_to_list=leader_to_list)
@connection
async def exportation(self,
with_default_owner: bool=False):
"""Export all values"""
exportation = await self._config_bag.context.cfgimpl_get_values()._p_.exportation()
exportation = await self._config_bag.context.cfgimpl_get_values()._p_.exportation(self._config_bag.connection)
if not with_default_owner:
exportation = [list(exportation[0]), list(exportation[1]), list(exportation[2]), list(exportation[3])]
index = exportation[0].index(None)
@ -901,29 +976,49 @@ class TiramisuContextValue(TiramisuConfig):
exportation[3].pop(index)
return exportation
@connection
async def importation(self, values):
"""Import values"""
cvalues = self._config_bag.context.cfgimpl_get_values()
connection = self._config_bag.connection
if None not in values[0]:
context_owner = await self._config_bag.context.cfgimpl_get_values().get_context_owner()
context_owner = await cvalues.get_context_owner(connection)
else:
context_owner = None
await self._config_bag.context.cfgimpl_get_values()._p_.importation(values)
await cvalues._p_.importation(connection,
values)
await self._config_bag.context.cfgimpl_reset_cache(None, None)
if context_owner is not None:
await self._config_bag.context.cfgimpl_get_values()._p_.setvalue(None,
None,
context_owner,
None,
True)
await cvalues._p_.setvalue(connection,
None,
None,
context_owner,
None,
True)
class TiramisuContextSession(TiramisuConfig):
"""Manage Config session"""
async def reset(self):
await self._config_bag.context.cfgimpl_get_values()._p_._storage.delete_session()
await self._config_bag.context.cfgimpl_get_settings()._p_._storage.delete_session()
async def list(self):
return await self._config_bag.context.cfgimpl_get_values()._p_._storage.list_sessions()
async def id(self):
"""Get config name"""
return self._config_bag.context.impl_getname()
class TiramisuContextOwner(TiramisuConfig):
"""Global owner"""
@connection
async def get(self):
"""Get owner"""
return await self._config_bag.context.cfgimpl_get_values().get_context_owner()
return await self._config_bag.context.cfgimpl_get_values().get_context_owner(self._config_bag.connection)
@connection
async def set(self, owner):
"""Set owner"""
try:
@ -931,85 +1026,109 @@ class TiramisuContextOwner(TiramisuConfig):
except AttributeError:
owners.addowner(owner)
obj_owner = getattr(owners, owner)
await self._config_bag.context.cfgimpl_get_values().set_context_owner(obj_owner)
values = self._config_bag.context.cfgimpl_get_values()
await values.set_context_owner(self._config_bag.connection,
obj_owner)
class TiramisuContextProperty(TiramisuConfig):
"""Manage config properties"""
@connection
async def read_only(self):
"""Set config to read only mode"""
old_props = self._config_bag.properties
settings = self._config_bag.context.cfgimpl_get_settings()
await settings.read_only(self._config_bag.context)
await self._reset_config_properties()
await settings.read_only(self._config_bag)
await self._reset_config_properties(self._config_bag.connection)
if 'force_store_value' not in old_props and \
'force_store_value' in self._config_bag.properties:
await self._force_store_value()
@connection
async def read_write(self):
"""Set config to read and write mode"""
old_props = self._config_bag.properties
settings = self._config_bag.context.cfgimpl_get_settings()
await settings.read_write(self._config_bag.context)
connection = self._config_bag.connection
await settings.read_write(self._config_bag)
or_properties = settings.rw_append - settings.ro_append - SPECIAL_PROPERTIES
permissives = frozenset(await settings.get_context_permissives() | or_properties)
await settings.set_context_permissives(permissives)
await self._reset_config_properties()
permissives = frozenset(await settings.get_context_permissives(connection) | or_properties)
await settings.set_context_permissives(connection,
permissives)
await self._reset_config_properties(connection)
if 'force_store_value' not in old_props and \
'force_store_value' in self._config_bag.properties:
await self._force_store_value()
@connection
async def add(self, prop):
"""Add a config property"""
settings = self._config_bag.context.cfgimpl_get_settings()
props = set(await self.get())
props.add(prop)
await self._set(frozenset(props))
if prop not in props:
props.add(prop)
await self._set(self._config_bag.connection, frozenset(props))
@connection
async def pop(self, prop):
"""Remove a config property"""
props = set(await self.get())
if prop in props:
props.remove(prop)
await self._set(frozenset(props))
await self._set(self._config_bag.connection, frozenset(props))
async def get(self):
async def get(self,
default=False):
"""Get all config properties"""
if default:
config = self._config_bag.context
async with config.getconnection() as connection:
properties = await config.cfgimpl_get_settings().get_context_properties(connection,
config._impl_properties_cache)
return self._config_bag.properties
async def _set(self, props):
async def _set(self,
connection,
props):
"""Personalise config properties"""
if 'force_store_value' in props:
force_store_value = 'force_store_value' not in self._config_bag.properties
else:
force_store_value = False
context = self._config_bag.context
await context.cfgimpl_get_settings().set_context_properties(props,
context)
await self._reset_config_properties()
await context.cfgimpl_get_settings().set_context_properties(self._config_bag.connection,
props,
self._config_bag.context)
await self._reset_config_properties(connection)
if force_store_value:
await self._force_store_value()
@connection
async def reset(self):
"""Remove config properties"""
context = self._config_bag.context
await context.cfgimpl_get_settings().reset(None,
context)
await self._reset_config_properties()
self._config_bag)
await self._reset_config_properties(self._config_bag.connection)
@connection
async def exportation(self):
"""Export config properties"""
return await self._config_bag.context.cfgimpl_get_settings()._p_.exportation()
return await self._config_bag.context.cfgimpl_get_settings()._p_.exportation(self._config_bag.connection)
@connection
async def importation(self, properties):
"""Import config properties"""
if 'force_store_value' in properties.get(None, {}).get(None, []):
force_store_value = 'force_store_value' not in self._config_bag.properties
else:
force_store_value = False
await self._config_bag.context.cfgimpl_get_settings()._p_.importation(properties)
settings = self._config_bag.context.cfgimpl_get_settings()
connection = self._config_bag.connection
await self._config_bag.context.cfgimpl_get_settings()._p_.importation(connection,
properties)
await self._config_bag.context.cfgimpl_reset_cache(None, None)
await self._reset_config_properties()
await self._reset_config_properties(connection)
if force_store_value:
await self._force_store_value()
@ -1067,43 +1186,58 @@ class TiramisuContextProperty(TiramisuConfig):
class TiramisuContextPermissive(TiramisuConfig):
"""Manage config permissives"""
@connection
async def get(self):
"""Get config permissives"""
return await self._config_bag.context.cfgimpl_get_settings().get_context_permissives()
return await self._get()
async def _set(self, permissives):
async def _get(self):
return await self._config_bag.context.cfgimpl_get_settings().get_context_permissives(self._config_bag.connection)
async def _set(self,
permissives):
"""Set config permissives"""
await self._config_bag.context.cfgimpl_get_settings().set_context_permissives(permissives)
await self._reset_config_properties()
connection = self._config_bag.connection
await self._config_bag.context.cfgimpl_get_settings().set_context_permissives(connection, permissives)
await self._reset_config_properties(connection)
@connection
async def exportation(self):
"""Export config permissives"""
return await self._config_bag.context.cfgimpl_get_settings()._pp_.exportation()
return await self._config_bag.context.cfgimpl_get_settings()._pp_.exportation(self._config_bag.connection)
@connection
async def importation(self, permissives):
"""Import config permissives"""
await self._config_bag.context.cfgimpl_get_settings()._pp_.importation(permissives)
settings = self._config_bag.context.cfgimpl_get_settings()
connection = self._config_bag.connection
await settings._pp_.importation(connection,
permissives)
await self._config_bag.context.cfgimpl_reset_cache(None,
None)
await self._reset_config_properties()
await self._reset_config_properties(connection)
@connection
async def reset(self):
"""Remove config permissives"""
context = self._config_bag.context
await context.cfgimpl_get_settings().reset_permissives(None,
context)
await self._reset_config_properties()
settings = context.cfgimpl_get_settings()
connection = self._config_bag.connection
await settings.reset_permissives(None,
self._config_bag)
await self._reset_config_properties(connection)
@connection
async def add(self, prop):
"""Add a config permissive"""
props = set(await self.get())
props = set(await self._get())
props.add(prop)
await self._set(frozenset(props))
@connection
async def pop(self, prop):
"""Remove a config permissive"""
props = set(await self.get())
props = set(await self._get())
if prop in props:
props.remove(prop)
await self._set(frozenset(props))
@ -1116,17 +1250,19 @@ class TiramisuContextOption(TiramisuConfig):
self._tiramisu_dict = None
super().__init__(*args, **kwargs)
@connection
async def find(self,
name,
value=undefined,
type=None,
first=False):
name,
value=undefined,
type=None,
first=False):
"""Find an or a list of options"""
options = []
async for path in self._config_bag.context.find(byname=name,
byvalue=value,
bytype=type,
config_bag=self._config_bag):
context = self._config_bag.context
async for path in context.find(byname=name,
byvalue=value,
bytype=type,
config_bag=self._config_bag):
option = TiramisuOption(path,
None,
self._config_bag)
@ -1184,10 +1320,11 @@ class TiramisuContextOption(TiramisuConfig):
self._config_bag))
return options
@connection
async def list(self,
type='option',
group_type=None,
recursive=False):
type='option',
group_type=None,
recursive=False):
"""List options (by default list only option)"""
assert type in ('all', 'option', 'optiondescription'), _('unknown list type {}').format(type)
assert group_type is None or isinstance(group_type, groups.GroupType), \
@ -1210,7 +1347,8 @@ class TiramisuContextOption(TiramisuConfig):
async def _load_dict(self,
clearable="all",
remotable="minimum"):
self._tiramisu_dict = TiramisuDict(await self._return_config(self._config_bag.context),
self._tiramisu_dict = TiramisuDict(await self._return_config(self._config_bag.context,
self._config_bag.context._storage),
root=None,
clearable=clearable,
remotable=remotable)
@ -1234,51 +1372,64 @@ class TiramisuContextOption(TiramisuConfig):
class _TiramisuContextConfigReset():
@connection
async def reset(self):
"""Remove all datas to current config (informations, values, properties, ...)"""
# Option's values
context_owner = await self._config_bag.context.cfgimpl_get_values().get_context_owner()
await self._config_bag.context.cfgimpl_get_values()._p_.importation(([], [], [], []))
await self._config_bag.context.cfgimpl_get_values()._p_.setvalue(None,
settings = self._config_bag.context.cfgimpl_get_settings()
connection = self._config_bag.connection
context_owner = await self._config_bag.context.cfgimpl_get_values().get_context_owner(connection)
await self._config_bag.context.cfgimpl_get_values()._p_.importation(connection, ([], [], [], []))
await self._config_bag.context.cfgimpl_get_values()._p_.setvalue(connection,
None,
None,
context_owner,
None,
True)
# Option's informations
await self._config_bag.context.cfgimpl_get_values()._p_.del_informations()
await self._config_bag.context.cfgimpl_get_values()._p_.del_informations(connection)
# Option's properties
await self._config_bag.context.cfgimpl_get_settings()._p_.importation({})
await self._config_bag.context.cfgimpl_get_settings()._p_.importation(connection, {})
# Option's permissives
await self._config_bag.context.cfgimpl_get_settings()._pp_.importation({})
await self._config_bag.context.cfgimpl_get_settings()._pp_.importation(connection, {})
# Remove cache
await self._config_bag.context.cfgimpl_reset_cache(None, None)
class _TiramisuContextConfig(TiramisuConfig, _TiramisuContextConfigReset):
"""Actions to Config"""
async def name(self):
return self._config_bag.context.impl_getname()
async def type(self):
"""Type a Config"""
return 'config'
async def copy(self,
session_id=None,
persistent=False,
storage=None):
"""Copy current config"""
return await self._return_config(await self._config_bag.context.duplicate(session_id,
persistent=persistent,
storage=storage))
if storage is None:
storage = self._config_bag.context._storage
async with self._config_bag.context.getconnection() as connection:
config = await self._config_bag.context.duplicate(connection,
session_id,
storage=storage)
return await self._return_config(config,
storage)
async def deepcopy(self,
session_id=None,
persistent=False,
storage=None,
metaconfig_prefix=None):
"""Copy current config with all parents"""
return await self._return_config(await self._config_bag.context.duplicate(session_id,
persistent=persistent,
storage=storage,
metaconfig_prefix=metaconfig_prefix,
deep=[]))
if storage is None:
storage = self._config_bag.context._storage
async with self._config_bag.context.getconnection() as connection:
config = await self._config_bag.context.duplicate(connection,
session_id,
storage=storage,
metaconfig_prefix=metaconfig_prefix,
deep=[])
return await self._return_config(config,
storage)
async def metaconfig(self):
"""Get first meta configuration (obsolete please use parents)"""
@ -1291,7 +1442,8 @@ class _TiramisuContextConfig(TiramisuConfig, _TiramisuContextConfigReset):
"""Get all parents of current config"""
ret = []
for parent in self._config_bag.context.get_parents():
ret.append(await self._return_config(parent))
ret.append(await self._return_config(parent,
parent._storage))
return ret
async def path(self):
@ -1301,17 +1453,19 @@ class _TiramisuContextConfig(TiramisuConfig, _TiramisuContextConfigReset):
class _TiramisuContextGroupConfig(TiramisuConfig):
"""Actions to GroupConfig"""
async def name(self):
"""Get config name"""
return self._config_bag.context.impl_getname()
async def type(self):
"""Type a Config"""
return 'groupconfig'
async def list(self):
"""List children's config"""
ret = []
for child in self._config_bag.context.cfgimpl_get_children():
ret.append(await self._return_config(child))
ret.append(await self._return_config(child,
child._storage))
return ret
@connection
async def find(self,
name: str,
value=undefined):
@ -1327,37 +1481,52 @@ class _TiramisuContextGroupConfig(TiramisuConfig):
config = self._config_bag.context
for spath in spaths:
config = config.getconfig(spath)
return self._return_config(config)
return self._return_config(config,
config._storage)
async def copy(self,
session_id=None,
persistent=False,
storage=None):
return await self._return_config(await self._config_bag.context.duplicate(session_id,
persistent=persistent,
storage=storage))
if storage is None:
storage = self._config_bag.context._storage
async with self._config_bag.context.getconnection() as connection:
config = await self._config_bag.context.duplicate(connection,
session_id,
storage=storage)
return await self._return_config(config,
storage)
async def deepcopy(self,
session_id=None,
persistent=False,
storage=None,
metaconfig_prefix=None):
return await self._return_config(await self._config_bag.context.duplicate(session_id,
persistent=persistent,
storage=storage,
metaconfig_prefix=metaconfig_prefix,
deep=[]))
if storage is None:
storage = self._config_bag.config._storage
async with self._config_bag.context.getconnection() as connection:
config = await self._config_bag.context.duplicate(connection,
session_id,
storage=storage,
metaconfig_prefix=metaconfig_prefix,
deep=[])
return await self._return_config(config,
storage)
async def path(self):
return self._config_bag.context.cfgimpl_get_config_path()
async def get(self,
name: str) -> 'Config':
return await self._return_config(self._config_bag.context.getconfig(name))
config = self._config_bag.context.getconfig(name)
return await self._return_config(config,
config._storage)
class _TiramisuContextMixConfig(_TiramisuContextGroupConfig, _TiramisuContextConfigReset):
"""Actions to MixConfig"""
async def type(self):
"""Type a Config"""
return 'mixconfig'
async def pop(self,
session_id=None,
config=None):
@ -1365,7 +1534,8 @@ class _TiramisuContextMixConfig(_TiramisuContextGroupConfig, _TiramisuContextCon
if __debug__ and None not in [session_id, config]:
raise APIError(_('cannot set session_id and config together'))
pop_config = await self._config_bag.context.pop_config(session_id=session_id, config=config)
return await self._return_config(pop_config)
return await self._return_config(pop_config,
pop_config._storage)
async def add(self,
config):
@ -1376,23 +1546,33 @@ class _TiramisuContextMixConfig(_TiramisuContextGroupConfig, _TiramisuContextCon
"""Get all parents of current config"""
ret = []
for parent in self._config_bag.context.get_parents():
ret.append(await self._return_config(parent))
ret.append(await self._return_config(parent,
parent._storage))
return ret
class _TiramisuContextMetaConfig(_TiramisuContextMixConfig):
"""Actions to MetaConfig"""
async def type(self):
"""Type a Config"""
return 'metaconfig'
async def new(self,
session_id,
persistent=False,
storage=None,
type='config'):
"""Create and add a new config"""
new_config = await self._config_bag.context.new_config(session_id=session_id,
persistent=persistent,
storage=storage,
type_=type)
return await self._return_config(new_config)
config = self._config_bag.context
if storage is None:
storage = config._storage
storage_obj = await storage.get()
async with storage_obj.Connection() as connection:
new_config = await config.new_config(connection,
session_id=session_id,
storage=storage,
type_=type)
return await self._return_config(new_config,
new_config._storage)
@ -1414,9 +1594,6 @@ class TiramisuAPI(TiramisuHelp):
def __init__(self,
config_bag,
orig_config_bags=None) -> None:
if not isinstance(config_bag, ConfigBag):
raise Exception('pfffff')
# config = await ConfigBag(context=config)
self._config_bag = config_bag
self._orig_config_bags = orig_config_bags
if not self._registers:
@ -1492,25 +1669,46 @@ class Config(TiramisuAPI):
async def __init__(self,
descr: OptionDescription,
session_id: str=None,
persistent: bool=False,
delete_old_session: bool=False,
storage=None,
display_name=None) -> None:
if isinstance(descr, KernelConfig):
config = descr
else:
config = await KernelConfig(descr,
session_id=session_id,
persistent=persistent,
storage=storage,
display_name=display_name)
settings = config.cfgimpl_get_settings()
properties = await settings.get_context_properties(config._impl_properties_cache)
permissives = await settings.get_context_permissives()
if storage is None:
storage = default_storage
storage_obj = await storage.get()
async with storage_obj.Connection() as connection:
if isinstance(descr, KernelConfig):
config = descr
else:
config = await KernelConfig(descr,
connection=connection,
session_id=session_id,
delete_old_session=delete_old_session,
storage=storage,
display_name=display_name)
settings = config.cfgimpl_get_settings()
properties = await settings.get_context_properties(connection,
config._impl_properties_cache)
permissives = await settings.get_context_permissives(connection)
config_bag = ConfigBag(config,
properties=properties,
permissives=permissives)
super().__init__(config_bag)
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
await self._config_bag.context.cfgimpl_get_values()._p_._storage.delete_session()
await self._config_bag.context.cfgimpl_get_settings()._p_._storage.delete_session()
def __del__(self):
try:
del self._config_bag.context
del self._config_bag
del self._orig_config_bags
except:
pass
@asyncinit
class MetaConfig(TiramisuAPI):
@ -1518,34 +1716,47 @@ class MetaConfig(TiramisuAPI):
async def __init__(self,
children: 'Config'=[],
session_id: Union[str, None]=None,
persistent: bool=False,
delete_old_session: bool=False,
optiondescription: Optional[OptionDescription]=None,
storage=None,
display_name=None) -> None:
if isinstance(children, KernelMetaConfig):
config = children
else:
_children = []
for child in children:
if isinstance(child, TiramisuAPI):
_children.append(child._config_bag.context)
else:
_children.append(child)
if storage is None:
storage = default_storage
storage_obj = await storage.get()
async with storage_obj.Connection() as connection:
if isinstance(children, KernelMetaConfig):
config = children
else:
_children = []
for child in children:
if isinstance(child, TiramisuAPI):
_children.append(child._config_bag.context)
else:
_children.append(child)
config = await KernelMetaConfig(_children,
session_id=session_id,
persistent=persistent,
optiondescription=optiondescription,
display_name=display_name,
storage=storage)
settings = config.cfgimpl_get_settings()
properties = await settings.get_context_properties(config._impl_properties_cache)
permissives = await settings.get_context_permissives()
config = await KernelMetaConfig(_children,
connection=connection,
session_id=session_id,
delete_old_session=delete_old_session,
optiondescription=optiondescription,
display_name=display_name,
storage=storage)
settings = config.cfgimpl_get_settings()
properties = await settings.get_context_properties(connection,
config._impl_properties_cache)
permissives = await settings.get_context_permissives(connection)
config_bag = ConfigBag(config,
properties=properties,
permissives=permissives)
super().__init__(config_bag)
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
await self._config_bag.context.cfgimpl_get_values()._p_._storage.delete_session()
await self._config_bag.context.cfgimpl_get_settings()._p_._storage.delete_session()
@asyncinit
class MixConfig(TiramisuAPI):
@ -1554,33 +1765,46 @@ class MixConfig(TiramisuAPI):
optiondescription: OptionDescription,
children: List[Config],
session_id: Optional[str]=None,
persistent: bool=False,
delete_old_session: bool=False,
storage=None,
display_name: Callable=None) -> None:
if isinstance(children, KernelMixConfig):
config = children
else:
_children = []
for child in children:
if isinstance(child, TiramisuAPI):
_children.append(child._config_bag.context)
else:
_children.append(child)
if storage is None:
storage = default_storage
storage_obj = await storage.get()
async with storage_obj.Connection() as connection:
if isinstance(children, KernelMixConfig):
config = children
else:
_children = []
for child in children:
if isinstance(child, TiramisuAPI):
_children.append(child._config_bag.context)
else:
_children.append(child)
config = await KernelMixConfig(optiondescription,
_children,
session_id=session_id,
persistent=persistent,
storage=storage,
display_name=display_name)
settings = config.cfgimpl_get_settings()
properties = await settings.get_context_properties(config._impl_properties_cache)
permissives = await settings.get_context_permissives()
config = await KernelMixConfig(optiondescription,
_children,
session_id=session_id,
delete_old_session=delete_old_session,
storage=storage,
connection=connection,
display_name=display_name)
settings = config.cfgimpl_get_settings()
properties = await settings.get_context_properties(connection,
config._impl_properties_cache)
permissives = await settings.get_context_permissives(connection)
config_bag = ConfigBag(config,
properties=properties,
permissives=permissives)
super().__init__(config_bag)
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc, tb):
await self._config_bag.context.cfgimpl_get_values()._p_._storage.delete_session()
await self._config_bag.context.cfgimpl_get_settings()._p_._storage.delete_session()
@asyncinit
class GroupConfig(TiramisuAPI):

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2019-2020 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
@ -20,10 +20,10 @@ from functools import wraps
def asyncinit(obj):
@wraps(obj.__new__)
@wraps(obj.__new__)
async def new(cls, *args, **kwargs):
instance = object.__new__(cls) # (cls, *args, **kwargs)
instance = object.__new__(cls) # (cls, *args, **kwargs)
await instance.__init__(*args, **kwargs)
return instance
return instance
obj.__new__ = new
return obj

View File

@ -1,4 +1,4 @@
# Copyright (C) 2012-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-2020 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
@ -150,8 +150,8 @@ class Calculation:
option_bag: OptionBag,
leadership_must_have_index: bool=False) -> str:
if not self.help_function:
return self.execute(option_bag,
leadership_must_have_index=leadership_must_have_index)
return await self.execute(option_bag,
leadership_must_have_index=leadership_must_have_index)
return await carry_out_calculation(option_bag.option,
callback=self.help_function,
callback_params=self.params,
@ -204,12 +204,8 @@ async def manager_callback(callbk: Union[ParamOption, ParamValue],
path = option.impl_getpath()
option_bag = await get_option_bag(config_bag,
option,
apply_index)
option_bag.config_bag.unrestraint()
option_bag.config_bag.remove_validation()
# if we are in properties calculation, cannot calculated properties
option_bag.properties = await config_bag.context.cfgimpl_get_settings().getproperties(option_bag,
apply_requires=False)
apply_index,
True)
new_value = await get_value(callbk, option_bag, path)
if apply_index is None and is_follower:
new_value[index] = value
@ -235,7 +231,8 @@ async def manager_callback(callbk: Union[ParamOption, ParamValue],
async def get_option_bag(config_bag,
opt,
index_):
index_,
self_calc):
# don't validate if option is option that we tried to validate
config_bag = config_bag.copy()
config_bag.properties = config_bag.true_properties - {'warnings'}
@ -245,7 +242,14 @@ async def manager_callback(callbk: Union[ParamOption, ParamValue],
option_bag.set_option(opt,
index_,
config_bag)
option_bag.properties = await config_bag.context.cfgimpl_get_settings().getproperties(option_bag)
if not self_calc:
option_bag.properties = await config_bag.context.cfgimpl_get_settings().getproperties(option_bag)
else:
option_bag.config_bag.unrestraint()
option_bag.config_bag.remove_validation()
# if we are in properties calculation, cannot calculated properties
option_bag.properties = await config_bag.context.cfgimpl_get_settings().getproperties(option_bag,
apply_requires=False)
return option_bag
if isinstance(callbk, ParamValue):
@ -294,7 +298,8 @@ async def manager_callback(callbk: Union[ParamOption, ParamValue],
path = callbk_option.impl_getpath()
option_bag = await get_option_bag(config_bag,
callbk_option,
index_)
index_,
False)
value = await get_value(callbk, option_bag, path)
if with_index:
value = value[index]

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-2020 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
@ -148,7 +148,7 @@ class SubConfig:
doption_bag.set_option(option,
option_bag.index,
option_bag.config_bag)
doption_bag.properties = await self.cfgimpl_get_settings().getproperties(doption_bag)
doption_bag.properties = None
await self.reset_one_option_cache(desc,
resetted_opts,
doption_bag)
@ -212,9 +212,7 @@ class SubConfig:
async def setattr(self,
value,
option_bag,
_commit=True):
option_bag):
if option_bag.option.impl_is_symlinkoption():
raise ConfigError(_("can't set value to a SymLinkOption"))
context = option_bag.config_bag.context
@ -223,22 +221,18 @@ class SubConfig:
raise LeadershipError(_('cannot reduce length of the leader "{}"'
'').format(option_bag.option.impl_get_display_name()))
return await context.cfgimpl_get_values().setvalue(value,
option_bag,
_commit)
option_bag)
async def delattr(self,
option_bag,
_commit=True):
option_bag):
option = option_bag.option
if option.impl_is_symlinkoption():
raise ConfigError(_("can't delete a SymLinkOption"))
values = self.cfgimpl_get_values()
if option_bag.index is not None:
await values.reset_follower(option_bag,
_commit)
await values.reset_follower(option_bag)
else:
await values.reset(option_bag,
_commit)
await values.reset(option_bag)
def _get_subpath(self, name):
if self._impl_path is None:
@ -261,7 +255,8 @@ class SubConfig:
name,
option_bag,
from_follower=False,
needs_re_verify_follower_properties=False):
needs_re_verify_follower_properties=False,
need_help=True):
"""
:return: option's value if name is an option name, OptionDescription
otherwise
@ -289,17 +284,20 @@ class SubConfig:
if not option.impl_is_follower() or \
(needs_re_verify_follower_properties and option_bag.index is not None) or \
(not needs_re_verify_follower_properties and (not from_follower or option_bag.index is None)):
await self.cfgimpl_get_settings().validate_properties(option_bag)
await self.cfgimpl_get_settings().validate_properties(option_bag,
need_help=need_help)
if option.impl_is_follower() and not from_follower:
length = await self.cfgimpl_get_length_leadership(option_bag)
follower_len = await self.cfgimpl_get_values()._p_.get_max_length(option_bag.path)
follower_len = await self.cfgimpl_get_values()._p_.get_max_length(option_bag.config_bag.connection,
option_bag.path)
if follower_len > length:
raise LeadershipError(_('the follower option "{}" has greater length ({}) than the leader '
'length ({})').format(option.impl_get_display_name(),
follower_len,
length,
option_bag.index))
if option.impl_is_follower() and option_bag.index is None:
value = []
for idx in range(length):
@ -307,8 +305,8 @@ class SubConfig:
soption_bag.set_option(option,
idx,
config_bag)
soption_bag.properties = await self.cfgimpl_get_settings().getproperties(soption_bag)
try:
soption_bag.properties = await self.cfgimpl_get_settings().getproperties(soption_bag)
value.append(await self.getattr(name,
soption_bag,
from_follower=True,
@ -422,7 +420,8 @@ class SubConfig:
pathsvalues,
leader_to_list):
for opt in await self.cfgimpl_get_description().get_children(config_bag):
if opt.impl_is_optiondescription() and leader_to_list and opt.impl_is_leadership():
if leader_to_list and opt.impl_is_optiondescription() and opt.impl_is_leadership():
# leader
children = await opt.get_children(config_bag)
leader = children[0]
loption_bag = OptionBag()
@ -453,27 +452,36 @@ class SubConfig:
foption_bag.set_option(follower_opt,
idx,
config_bag)
foption_bag.properties = await self.cfgimpl_get_settings().getproperties(foption_bag)
await subconfig._make_sub_dict(leadership_pathsvalues,
leader_currpath,
foption_bag,
flatten,
fullpath,
leader_to_list)
try:
foption_bag.properties = await self.cfgimpl_get_settings().getproperties(foption_bag)
await subconfig._make_sub_dict(leadership_pathsvalues,
leader_currpath,
foption_bag,
flatten,
fullpath,
leader_to_list)
except PropertiesOptionError as err:
if err.proptype in (['mandatory'], ['empty']):
raise err
continue
pathsvalues[leader_name].append(leadership_pathsvalues)
else:
soption_bag = OptionBag()
soption_bag.set_option(opt,
None,
config_bag)
soption_bag.properties = await self.cfgimpl_get_settings().getproperties(soption_bag)
await self._make_sub_dict(pathsvalues,
_currpath,
soption_bag,
flatten,
fullpath,
leader_to_list)
return pathsvalues
try:
soption_bag.properties = await self.cfgimpl_get_settings().getproperties(soption_bag)
await self._make_sub_dict(pathsvalues,
_currpath,
soption_bag,
flatten,
fullpath,
leader_to_list)
except PropertiesOptionError as err:
if err.proptype in (['mandatory'], ['empty']):
raise err
continue
async def _make_sub_dict(self,
pathsvalues,
@ -485,31 +493,22 @@ class SubConfig:
option = option_bag.option
name = option.impl_getname()
if option.impl_is_optiondescription():
try:
await self.cfgimpl_get_settings().validate_properties(option_bag)
subconfig = await SubConfig(option_bag.option,
self._impl_context,
option_bag.config_bag,
option_bag.path)
await subconfig._make_dict(option_bag.config_bag,
_currpath + [name],
flatten,
fullpath,
pathsvalues,
leader_to_list)
except PropertiesOptionError as err:
if err.proptype in (['mandatory'], ['empty']):
raise err
await self.cfgimpl_get_settings().validate_properties(option_bag,
need_help=False)
subconfig = await SubConfig(option_bag.option,
self._impl_context,
option_bag.config_bag,
option_bag.path)
await subconfig._make_dict(option_bag.config_bag,
_currpath + [name],
flatten,
fullpath,
pathsvalues,
leader_to_list)
else:
try:
ret = await self.getattr(name,
option_bag)
except PropertiesOptionError as err:
# import traceback
# traceback.print_exc()
if err.proptype in (['mandatory'], ['empty']):
raise err
return
ret = await self.getattr(name,
option_bag,
need_help=False)
if flatten:
name_ = option.impl_getname()
elif fullpath:
@ -551,6 +550,7 @@ class _CommonConfig(SubConfig):
# information
async def impl_set_information(self,
connection,
key,
value):
"""updates the information's attribute
@ -558,48 +558,56 @@ class _CommonConfig(SubConfig):
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
await self._impl_values.set_information(key,
await self._impl_values.set_information(connection,
key,
value)
async def impl_get_information(self,
connection,
key,
default=undefined):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
return await self._impl_values.get_information(key,
return await self._impl_values.get_information(connection,
key,
default)
async def impl_del_information(self,
connection,
key,
raises=True):
await self._impl_values.del_information(key,
await self._impl_values.del_information(connection,
key,
raises)
async def impl_list_information(self):
return await self._impl_values.list_information()
async def impl_list_information(self,
connection):
return await self._impl_values.list_information(connection)
def __getstate__(self):
raise NotImplementedError()
async def _gen_fake_values(self):
async def _gen_fake_values(self,
connection):
fake_config = await KernelConfig(self._impl_descr,
persistent=False,
force_values=await get_default_values_storages(),
force_values=await get_default_values_storages(connection),
force_settings=self.cfgimpl_get_settings(),
display_name=self._display_name)
export = await self.cfgimpl_get_values()._p_.exportation()
await fake_config.cfgimpl_get_values()._p_.importation(export)
display_name=self._display_name,
connection=connection)
export = await self.cfgimpl_get_values()._p_.exportation(connection)
await fake_config.cfgimpl_get_values()._p_.importation(connection,
export)
fake_config.parents = self.parents
return fake_config
async def duplicate(self,
connection,
session_id=None,
force_values=None,
force_settings=None,
storage=None,
persistent=False,
metaconfig_prefix=None,
child=None,
deep=None):
@ -610,8 +618,8 @@ class _CommonConfig(SubConfig):
session_id=session_id,
force_values=force_values,
force_settings=force_settings,
persistent=persistent,
storage=storage,
connection=connection,
display_name=self._display_name)
else:
if session_id is None and metaconfig_prefix is not None:
@ -620,15 +628,18 @@ class _CommonConfig(SubConfig):
_duplicate=True,
optiondescription=self._impl_descr,
session_id=session_id,
persistent=persistent,
storage=storage,
connection=connection,
display_name=self._display_name)
duplicated_values = duplicated_config.cfgimpl_get_values()
duplicated_settings = duplicated_config.cfgimpl_get_settings()
await duplicated_values._p_.importation(await self.cfgimpl_get_values()._p_.exportation())
properties = await self.cfgimpl_get_settings()._p_.exportation()
await duplicated_settings._p_.importation(properties)
await duplicated_settings._pp_.importation(await self.cfgimpl_get_settings()._pp_.exportation())
await duplicated_values._p_.importation(connection,
await self.cfgimpl_get_values()._p_.exportation(connection))
properties = await self.cfgimpl_get_settings()._p_.exportation(connection)
await duplicated_settings._p_.importation(connection,
properties)
await duplicated_settings._pp_.importation(connection,
await self.cfgimpl_get_settings()._pp_.exportation(connection))
duplicated_settings.ro_append = self.cfgimpl_get_settings().ro_append
duplicated_settings.rw_append = self.cfgimpl_get_settings().rw_append
duplicated_settings.ro_remove = self.cfgimpl_get_settings().ro_remove
@ -644,11 +655,11 @@ class _CommonConfig(SubConfig):
wparent = parent()
if wparent not in deep:
deep.append(wparent)
duplicated_config = await wparent.duplicate(deep=deep,
duplicated_config = await wparent.duplicate(connection,
deep=deep,
storage=storage,
metaconfig_prefix=metaconfig_prefix,
child=duplicated_config,
persistent=persistent)
child=duplicated_config)
else:
duplicated_config.parents = self.parents
for parent in self.parents:
@ -672,13 +683,15 @@ class KernelConfig(_CommonConfig):
__slots__ = ('__weakref__',
'_impl_name',
'_display_name',
'_impl_symlink')
'_impl_symlink',
'_storage')
impl_type = 'config'
async def __init__(self,
descr,
connection,
session_id=None,
persistent=False,
delete_old_session=False,
force_values=None,
force_settings=None,
display_name=None,
@ -690,11 +703,8 @@ class KernelConfig(_CommonConfig):
:type descr: an instance of ``option.OptionDescription``
:param context: the current root config
:type context: `Config`
:param session_id: session ID is import with persistent Config to
retrieve good session
:param session_id: name of the session
:type session_id: `str`
:param persistent: if persistent, don't delete storage when leaving
:type persistent: `boolean`
"""
self.parents = []
self._impl_symlink = []
@ -711,21 +721,25 @@ class KernelConfig(_CommonConfig):
self._impl_settings = force_settings
self._impl_permissives_cache = Cache()
self._impl_properties_cache = Cache()
self._impl_values = await Values(force_values)
self._impl_values = await Values(force_values,
connection)
self._impl_values_cache = Cache()
else:
properties, permissives, values, session_id = await get_storages(self,
session_id,
persistent,
storage=storage)
storage, properties, permissives, values, session_id = await get_storages(self,
session_id,
delete_old_session,
storage,
connection)
if not valid_name(session_id):
raise ValueError(_("invalid session ID: {0} for config").format(session_id))
self._impl_settings = Settings(properties,
permissives)
self._impl_permissives_cache = Cache()
self._impl_properties_cache = Cache()
self._impl_values = await Values(values)
self._impl_values = await Values(values,
connection)
self._impl_values_cache = Cache()
self._storage = storage
self._impl_context = weakref.ref(self)
await super().__init__(descr,
self._impl_context,
@ -738,6 +752,9 @@ class KernelConfig(_CommonConfig):
def impl_getname(self):
return self._impl_name
def getconnection(self):
return self.cfgimpl_get_settings()._p_.getconnection()
@asyncinit
class KernelGroupConfig(_CommonConfig):
@ -803,17 +820,10 @@ class KernelGroupConfig(_CommonConfig):
index,
value,
config_bag,
only_config=False,
_commit=True):
only_config=False):
"""Setattr not in current KernelGroupConfig, but in each children
"""
ret = []
if self.impl_type == 'group':
# No value so cannot commit only one time
commit = True
else:
# Commit only one time
commit = False
for child in self._impl_children:
cconfig_bag = config_bag.copy()
cconfig_bag.context = child
@ -822,12 +832,12 @@ class KernelGroupConfig(_CommonConfig):
index,
value,
cconfig_bag,
only_config=only_config,
_commit=commit))
only_config=only_config))
else:
settings = child.cfgimpl_get_settings()
properties = await settings.get_context_properties(child._impl_properties_cache)
permissives = await settings.get_context_permissives()
properties = await settings.get_context_properties(config_bag.connection,
child._impl_properties_cache)
permissives = await settings.get_context_permissives(config_bag.connection)
cconfig_bag.properties = properties
cconfig_bag.permissives = permissives
try:
@ -842,8 +852,7 @@ class KernelGroupConfig(_CommonConfig):
cconfig_bag)
option_bag.properties = await settings.getproperties(option_bag)
await child.setattr(value,
option_bag,
_commit=commit)
option_bag)
except PropertiesOptionError as err:
ret.append(PropertiesOptionError(err._option_bag,
err.proptype,
@ -853,8 +862,6 @@ class KernelGroupConfig(_CommonConfig):
err._orig_opt))
except (ValueError, LeadershipError, AttributeError) as err:
ret.append(err)
if _commit and self.impl_type != 'group':
await self.cfgimpl_get_values()._p_.commit()
return ret
@ -896,8 +903,9 @@ class KernelGroupConfig(_CommonConfig):
cconfig_bag = config_bag.copy()
cconfig_bag.context = child
settings = child.cfgimpl_get_settings()
properties = await settings.get_context_properties(child._impl_properties_cache)
permissives = await settings.get_context_permissives()
properties = await settings.get_context_properties(config_bag.connection,
child._impl_properties_cache)
permissives = await settings.get_context_permissives(config_bag.connection)
cconfig_bag.properties = properties
cconfig_bag.permissives = permissives
async for path in child.find(None,
@ -918,15 +926,17 @@ class KernelGroupConfig(_CommonConfig):
return self._impl_name
async def reset(self,
path,
_commit=True):
connection,
path):
for child in self._impl_children:
settings = child.cfgimpl_get_settings()
properties = await settings.get_context_properties(child._impl_properties_cache)
permissives = await settings.get_context_permissives()
properties = await settings.get_context_properties(connection,
child._impl_properties_cache)
permissives = await settings.get_context_permissives(connection)
config_bag = ConfigBag(child,
properties=properties,
permissives=permissives)
config_bag.connection = connection
config_bag.remove_validation()
subconfig, name = await child.cfgimpl_get_home_by_path(path,
config_bag)
@ -939,8 +949,7 @@ class KernelGroupConfig(_CommonConfig):
config_bag)
option_bag.properties = await child.cfgimpl_get_settings().getproperties(option_bag)
option_bag.config_bag.context = child
await child.cfgimpl_get_values().reset(option_bag,
_commit=_commit)
await child.cfgimpl_get_values().reset(option_bag)
def getconfig(self,
name):
@ -949,18 +958,26 @@ class KernelGroupConfig(_CommonConfig):
return child
raise ConfigError(_('unknown config "{}"').format(name))
def getconnection(self):
if self.impl_type == 'group':
# Get the first storage, assume that all children have same storage
return self._impl_children[0].getconnection()
return self.cfgimpl_get_settings()._p_.getconnection()
@asyncinit
class KernelMixConfig(KernelGroupConfig):
__slots__ = ('_display_name',
'_impl_symlink')
'_impl_symlink',
'_storage')
impl_type = 'mix'
async def __init__(self,
optiondescription,
children,
connection,
session_id=None,
persistent=False,
delete_old_session=False,
storage=None,
display_name=None,
_duplicate=False):
@ -971,16 +988,19 @@ class KernelMixConfig(KernelGroupConfig):
if not isinstance(child, (KernelConfig, KernelMixConfig)):
raise TypeError(_("child must be a Config, MixConfig or MetaConfig"))
child.parents.append(weakref.ref(self))
properties, permissives, values, session_id = await get_storages(self,
session_id,
persistent,
storage=storage)
storage, properties, permissives, values, session_id = await get_storages(self,
session_id,
delete_old_session,
storage,
connection)
self._impl_settings = Settings(properties,
permissives)
self._impl_permissives_cache = Cache()
self._impl_properties_cache = Cache()
self._impl_values = await Values(values)
self._impl_values = await Values(values,
connection)
self._impl_values_cache = Cache()
self._storage = storage
await super().__init__(children,
session_id=session_id,
_descr=optiondescription)
@ -994,8 +1014,7 @@ class KernelMixConfig(KernelGroupConfig):
force_default=False,
force_dont_change_value=False,
force_default_if_same=False,
only_config=False,
_commit=True):
only_config=False):
"""only_config: could be set if you want modify value in all Config included in
this KernelMetaConfig
"""
@ -1008,8 +1027,7 @@ class KernelMixConfig(KernelGroupConfig):
index,
value,
config_bag,
only_config=only_config,
_commit=_commit)
only_config=only_config)
ret = []
subconfig, name = await self.cfgimpl_get_home_by_path(path,
config_bag)
@ -1029,8 +1047,9 @@ class KernelMixConfig(KernelGroupConfig):
cconfig_bag = config_bag.copy()
cconfig_bag.context = child
settings = child.cfgimpl_get_settings()
properties = await settings.get_context_properties(child._impl_properties_cache)
permissives = await settings.get_context_permissives()
properties = await settings.get_context_properties(config_bag.connection,
child._impl_properties_cache)
permissives = await settings.get_context_permissives(config_bag.connection)
cconfig_bag.properties = properties
cconfig_bag.permissives = permissives
try:
@ -1053,22 +1072,21 @@ class KernelMixConfig(KernelGroupConfig):
cconfig_bag)
moption_bag.properties = await settings.getproperties(moption_bag)
if force_default_if_same:
if not await child.cfgimpl_get_values()._p_.hasvalue(path):
if not await child.cfgimpl_get_values()._p_.hasvalue(config_bag.connection,
path):
child_value = undefined
else:
child_value = await subconfig2.getattr(name,
moption_bag)
if force_default or (force_default_if_same and value == child_value):
await child.cfgimpl_get_values().reset(moption_bag,
_commit=False)
await child.cfgimpl_get_values().reset(moption_bag)
continue
if force_dont_change_value:
child_value = await child.getattr(name,
moption_bag)
if value != child_value:
await subconfig2.setattr(child_value,
moption_bag,
_commit=False)
moption_bag)
except PropertiesOptionError as err:
ret.append(PropertiesOptionError(err._option_bag,
err.proptype,
@ -1089,8 +1107,7 @@ class KernelMixConfig(KernelGroupConfig):
else:
moption_bag = option_bag
await subconfig.setattr(value,
moption_bag,
_commit=False)
moption_bag)
except (PropertiesOptionError, ValueError, LeadershipError) as err:
ret.append(err)
return ret
@ -1098,8 +1115,7 @@ class KernelMixConfig(KernelGroupConfig):
async def reset(self,
path,
only_children,
config_bag,
commit=True):
config_bag):
rconfig_bag = config_bag.copy()
rconfig_bag.remove_validation()
if self.impl_type == 'meta':
@ -1144,21 +1160,16 @@ class KernelMixConfig(KernelGroupConfig):
None,
rconfig_bag)
moption_bag.properties = await self.cfgimpl_get_settings().getproperties(moption_bag)
await child.cfgimpl_get_values().reset(moption_bag,
_commit=False)
await child.cfgimpl_get_values().reset(moption_bag)
except AttributeError:
pass
if isinstance(child, KernelMixConfig):
await child.reset(path,
False,
rconfig_bag,
commit=False)
rconfig_bag)
if not only_children:
option_bag.config_bag = config_bag
await self.cfgimpl_get_values().reset(option_bag,
_commit=False)
if commit:
await self.cfgimpl_get_values()._p_.commit()
await self.cfgimpl_get_values().reset(option_bag)
async def add_config(self,
apiconfig):
@ -1201,8 +1212,9 @@ class KernelMetaConfig(KernelMixConfig):
async def __init__(self,
children,
connection,
session_id=None,
persistent=False,
delete_old_session=False,
optiondescription=None,
storage=None,
display_name=None,
@ -1217,7 +1229,8 @@ class KernelMetaConfig(KernelMixConfig):
' must have string has child, '
'not {}').format(child_session_id)
new_children.append(await KernelConfig(optiondescription,
persistent=persistent,
connection,
delete_old_session=delete_old_session,
session_id=child_session_id,
display_name=self._display_name))
children = new_children
@ -1233,46 +1246,51 @@ class KernelMetaConfig(KernelMixConfig):
'have the same optiondescription'))
await super().__init__(descr,
children,
persistent=persistent,
connection,
delete_old_session=delete_old_session,
storage=storage,
session_id=session_id)
async def new_config(self,
connection,
session_id,
type_='config',
persistent=False,
storage=None):
if session_id in [child.impl_getname() for child in self._impl_children]:
raise ConflictError(_('config name must be uniq in '
'groupconfig for {0}').format(session_id))
assert type_ in ('config', 'metaconfig', 'mixconfig'), _('unknown type {}').format(type_)
new = not persistent or session_id not in list_sessions()
new = session_id not in await list_sessions()
if type_ == 'config':
config = await KernelConfig(self._impl_descr,
session_id=session_id,
persistent=persistent,
storage=storage,
connection=connection,
display_name=self._display_name)
elif type_ == 'metaconfig':
config = await KernelMetaConfig([],
optiondescription=self._impl_descr,
session_id=session_id,
persistent=persistent,
storage=storage,
connection=connection,
display_name=self._display_name)
elif type_ == 'mixconfig':
config = await KernelMixConfig(children=[],
optiondescription=self._impl_descr,
session_id=session_id,
persistent=persistent,
storage=storage,
connection=connection,
display_name=self._display_name)
# Copy context properties/permissives
if new:
settings = config.cfgimpl_get_settings()
properties = await self.cfgimpl_get_settings().get_context_properties(config._impl_properties_cache)
await settings.set_context_properties(properties, config)
await settings.set_context_permissives(await self.cfgimpl_get_settings().get_context_permissives())
properties = await self.cfgimpl_get_settings().get_context_properties(connection,
config._impl_properties_cache)
await settings.set_context_properties(connection,
properties,
config)
await settings.set_context_permissives(connection,
await self.cfgimpl_get_settings().get_context_permissives(connection))
settings.ro_append = self.cfgimpl_get_settings().ro_append
settings.rw_append = self.cfgimpl_get_settings().rw_append
settings.ro_remove = self.cfgimpl_get_settings().ro_remove

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2012-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-2020 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
@ -57,7 +57,8 @@ class PropertiesOptionError(AttributeError):
settings,
opt_type=None,
name=None,
orig_opt=None):
orig_opt=None,
help_properties=None):
if opt_type:
self._opt_type = opt_type
self._name = name
@ -71,6 +72,7 @@ class PropertiesOptionError(AttributeError):
self._orig_opt = None
self._option_bag = option_bag
self.proptype = proptype
self.help_properties = help_properties
self._settings = settings
self.msg = None
super().__init__(None)
@ -84,12 +86,10 @@ class PropertiesOptionError(AttributeError):
return self.msg
if self._settings is None:
return 'error'
#for property_ in self._settings.get_calculated_properties(self._option_bag):
# prop = property_.help(self._option_bag)
# if prop is not None:
# properties.append(prop)
properties = list(self.proptype)
if self.help_properties:
properties = list(self.help_properties)
else:
properties = list(self.proptype)
only_one = len(properties) == 1
properties_msg = display_list(properties, add_quote=True)
if only_one:

View File

@ -1,4 +1,4 @@
# Copyright (C) 2018-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2018-2020 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
@ -510,8 +510,8 @@ class CalcValuePropertyHelp(CalcValue):
msgs.append(self.build_arg(name, f'"{value}"'))
msg = display_list(msgs, self.condition_operator.lower())
else:
return [f'"{action}"']
return [f'"{action}" ({msg})']
return [(action, f'"{action}"')]
return [(action, f'"{action}" ({msg})')]
return
## calc_properties.setdefault(action, []).append(msg)

View File

@ -1,5 +1,5 @@
# -*- coding: UTF-8 -*-
# Copyright (C) 2012-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-2020 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

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"logger for tiramisu"
# Copyright (C) 2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2019-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2014-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2014-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"Leadership support"
# Copyright (C) 2014-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2014-2020 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
@ -113,8 +113,7 @@ class Leadership(OptionDescription):
async def reset(self,
values: Values,
option_bag: OptionBag,
_commit: bool=True) -> None:
option_bag: OptionBag) -> None:
config_bag = option_bag.config_bag.copy()
config_bag.remove_validation()
for follower in self.get_followers():
@ -123,15 +122,13 @@ class Leadership(OptionDescription):
None,
config_bag)
soption_bag.properties = await config_bag.context.cfgimpl_get_settings().getproperties(soption_bag)
await values.reset(soption_bag,
_commit=_commit)
await values.reset(soption_bag)
async def follower_force_store_value(self,
values,
value,
option_bag,
owner,
_commit,
dyn=None) -> None:
settings = option_bag.config_bag.context.cfgimpl_get_settings()
if value:
@ -152,8 +149,7 @@ class Leadership(OptionDescription):
foption_bag.properties = await settings.getproperties(foption_bag)
await values._setvalue(foption_bag,
await values.getvalue(foption_bag),
owner,
commit=False)
owner)
async def pop(self,
values: Values,
@ -167,7 +163,8 @@ class Leadership(OptionDescription):
config_bag.remove_validation()
for follower in followers:
follower_path = follower.impl_getpath()
followerlen = await values._p_.get_max_length(follower_path)
followerlen = await values._p_.get_max_length(config_bag.connection,
follower_path)
soption_bag = OptionBag()
soption_bag.set_option(follower,
index,
@ -177,13 +174,16 @@ class Leadership(OptionDescription):
is_default = await values.is_default_owner(soption_bag,
validate_meta=False)
if not is_default and followerlen > index:
await values._p_.resetvalue_index(follower_path,
index,
True)
await values._p_.resetvalue_index(config_bag.connection,
follower_path,
index)
if followerlen > index + 1:
for idx in range(index + 1, followerlen):
if await values._p_.hasvalue(follower_path, idx):
await values._p_.reduce_index(follower_path,
if await values._p_.hasvalue(config_bag.connection,
follower_path,
idx):
await values._p_.reduce_index(config_bag.connection,
follower_path,
idx)
def reset_cache(self,

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"option types and option description"
# Copyright (C) 2012-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2014-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2014-2020 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
@ -109,10 +109,10 @@ class CacheOptionDescription(BaseOption):
config_bag: ConfigBag) -> None:
if 'force_store_value' not in config_bag.properties:
return
commit = False
values = config_bag.context.cfgimpl_get_values()
for subpath, option in self._cache_force_store_values:
if not await values._p_.hasvalue(subpath):
if not await values._p_.hasvalue(config_bag.connection,
subpath):
if option.impl_is_follower():
option_bag = OptionBag()
leader = option.impl_get_leadership().get_leader()
@ -127,7 +127,8 @@ class CacheOptionDescription(BaseOption):
index,
config_bag)
option_bag.properties = frozenset()
await values._p_.setvalue(subpath,
await values._p_.setvalue(config_bag.connection,
subpath,
await values.getvalue(option_bag),
owners.forced,
index,
@ -138,15 +139,12 @@ class CacheOptionDescription(BaseOption):
None,
config_bag)
option_bag.properties = frozenset()
await values._p_.setvalue(subpath,
await values._p_.setvalue(config_bag.connection,
subpath,
await values.getvalue(option_bag),
owners.forced,
None,
False)
commit = True
if commit:
await values._p_.commit()
class OptionDescriptionWalk(CacheOptionDescription):

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2018-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2018-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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
@ -103,7 +103,7 @@ class SynDynOptionDescription:
config_bag,
self):
yield option
def impl_getpath(self) -> str:
subpath = self._subpath
if subpath != '':
@ -150,11 +150,9 @@ class SynDynLeadership(SynDynOptionDescription):
values,
value,
option_bag,
owner,
_commit) -> None:
owner) -> None:
await self._opt.follower_force_store_value(values,
value,
option_bag,
owner,
_commit,
dyn=self)

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2017-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2017-2020 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

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"sets the options of the configuration objects Config object itself"
# Copyright (C) 2012-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2012-2020 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
@ -192,7 +192,8 @@ class ConfigBag:
'true_properties', # properties for current context
'is_unrestraint',
'permissives', # permissives for current context
'expiration_time' # EXPIRATION_TIME
'expiration_time', # EXPIRATION_TIME
'connection',
)
def __init__(self,
@ -236,7 +237,10 @@ class ConfigBag:
def copy(self):
kwargs = {}
for key in self.__slots__:
kwargs[key] = getattr(self, key)
try:
kwargs[key] = getattr(self, key)
except KeyError:
pass
return ConfigBag(**kwargs)
@ -374,9 +378,6 @@ class Settings(object):
:param context: the root config
:param storage: the storage type
- dictionary -> in memory
- sqlite3 -> persistent
"""
# generic owner
self._p_ = properties
@ -391,6 +392,7 @@ class Settings(object):
# get properties and permissive methods
async def get_context_properties(self,
connection,
cache):
is_cached, props, validated = cache.getcache(None,
None,
@ -399,7 +401,8 @@ class Settings(object):
{},
'context_props')
if not is_cached:
props = await self._p_.getproperties(None,
props = await self._p_.getproperties(connection,
None,
None,
self.default_properties)
cache.setcache(None,
@ -413,7 +416,8 @@ class Settings(object):
async def getproperties(self,
option_bag,
apply_requires=True,
uncalculated=False):
uncalculated=False,
help_property=False):
"""
"""
option = option_bag.option
@ -422,44 +426,58 @@ class Settings(object):
option = option.impl_getopt()
path = option.impl_getpath()
index = option_bag.index
if apply_requires and not uncalculated:
if apply_requires and not uncalculated and not help_property:
cache = config_bag.context._impl_properties_cache
config_bag_props = config_bag.properties
is_cached, props, validated = cache.getcache(path,
config_bag.expiration_time,
index,
config_bag_props,
config_bag.properties,
{},
'self_props')
else:
is_cached = False
if not is_cached:
props = set()
p_props = await self._p_.getproperties(path,
# if index, get option's properties (without index) too
p_props = await self._p_.getproperties(config_bag.connection,
path,
None,
option.impl_getproperties())
if index is not None:
p_props = chain(p_props,
await self._p_.getproperties(path,
await self._p_.getproperties(config_bag.connection,
path,
index,
option.impl_getproperties()))
for prop in p_props:
if uncalculated or isinstance(prop, str):
props.add(prop)
if not help_property:
props.add(prop)
else:
props.add((prop, prop))
elif apply_requires:
new_prop = await prop.execute(option_bag,
leadership_must_have_index=True)
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)
if new_prop is None:
continue
elif not isinstance(new_prop, str):
elif (not help_property and not isinstance(new_prop, str)) or \
(help_property and not isinstance(new_prop, tuple)):
raise ValueError(_('invalid property type {} for {} with {} function').format(type(new_prop),
option_bag.option.impl_getname(),
prop.function.__name__))
if not option.impl_is_optiondescription() and option.impl_is_leader() and new_prop not in ALLOWED_LEADER_PROPERTIES:
if not option.impl_is_optiondescription() and \
option.impl_is_leader() and \
new_prop not in ALLOWED_LEADER_PROPERTIES:
raise LeadershipError(_('leader cannot have "{}" property').format(new_prop))
props.add(new_prop)
props -= await self.getpermissives(option_bag)
if not uncalculated and apply_requires and not config_bag.is_unrestraint:
if not uncalculated and apply_requires and not config_bag.is_unrestraint and not help_property:
cache.setcache(path,
index,
props,
@ -468,36 +486,20 @@ class Settings(object):
True)
return props
async def get_calculated_properties(self,
option_bag):
option = option_bag.option
if option.impl_is_symlinkoption():
option = option.impl_getopt()
path = option.impl_getpath()
p_props = await self._p_.getproperties(path,
None,
option.impl_getproperties())
if option_bag.index is not None:
p_props = chain(p_props,
await self._p_.getproperties(path,
option_bag.index,
option.impl_getproperties()))
for prop in p_props:
if not isinstance(prop, str):
yield prop
async def has_properties_index(self,
option_bag):
option = option_bag.option
if option.impl_is_symlinkoption():
option = option.impl_getopt()
path = option.impl_getpath()
p_props = await self._p_.getproperties(path,
p_props = await self._p_.getproperties(option_bag.config_bag.connection,
path,
None,
option.impl_getproperties())
if option_bag.index is not None:
p_props = chain(p_props,
await self._p_.getproperties(path,
await self._p_.getproperties(option_bag.config_bag.connection,
path,
option_bag.index,
option.impl_getproperties()))
for prop in p_props:
@ -505,11 +507,14 @@ class Settings(object):
return True
return False
async def get_context_permissives(self):
return await self.getpermissives(None)
async def get_context_permissives(self,
connection):
return await self.getpermissives(None,
connection=connection)
async def getpermissives(self,
option_bag):
option_bag,
connection=None):
if option_bag is None:
path = None
index = None
@ -521,18 +526,25 @@ class Settings(object):
else:
path = option_bag.path
index = option_bag.index
permissives = await self._pp_.getpermissives(path, None)
connection = option_bag.config_bag.connection
permissives = await self._pp_.getpermissives(connection,
path,
None)
if index is not None:
option_permissives = await self._pp_.getpermissives(path, index)
option_permissives = await self._pp_.getpermissives(connection,
path,
index)
permissives = frozenset(option_permissives | permissives)
return permissives
#____________________________________________________________
# set methods
async def set_context_properties(self,
connection,
properties,
context):
await self._p_.setproperties(None,
await self._p_.setproperties(connection,
None,
None,
properties)
await context.cfgimpl_reset_cache(None)
@ -561,7 +573,8 @@ class Settings(object):
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()))
await self._p_.setproperties(path,
await self._p_.setproperties(option_bag.config_bag.connection,
path,
option_bag.index,
properties)
# values too because of follower values could have a PropertiesOptionError has value
@ -569,13 +582,16 @@ class Settings(object):
option_bag.properties = properties
async def set_context_permissives(self,
connection,
permissives):
await self.setpermissives(None,
permissives)
permissives,
connection=connection)
async def setpermissives(self,
option_bag,
permissives):
permissives,
connection=None):
"""
enables us to put the permissives in the storage
@ -594,6 +610,7 @@ class Settings(object):
"").format(opt.impl_get_display_name()))
path = option_bag.path
index = option_bag.index
connection = option_bag.config_bag.connection
else:
path = None
index = None
@ -601,7 +618,10 @@ class Settings(object):
if forbidden_permissives:
raise ConfigError(_('cannot add those permissives: {0}').format(
' '.join(forbidden_permissives)))
await self._pp_.setpermissives(path, index, permissives)
await self._pp_.setpermissives(connection,
path,
index,
permissives)
if option_bag is not None:
await option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
@ -610,7 +630,7 @@ class Settings(object):
async def reset(self,
option_bag,
context):
config_bag):
if option_bag is None:
opt = None
path = None
@ -622,12 +642,14 @@ class Settings(object):
"").format(opt.impl_get_display_name())
path = option_bag.path
index = option_bag.index
await self._p_.delproperties(path, index)
await context.cfgimpl_reset_cache(option_bag)
await self._p_.delproperties(config_bag.connection,
path,
index)
await config_bag.context.cfgimpl_reset_cache(option_bag)
async def reset_permissives(self,
option_bag,
context):
config_bag):
if option_bag is None:
opt = None
path = None
@ -639,8 +661,10 @@ class Settings(object):
"").format(opt.impl_get_display_name())
index = option_bag.index
path = option_bag.path
await self._pp_.delpermissive(path, index)
await context.cfgimpl_reset_cache(option_bag)
await self._pp_.delpermissive(config_bag.connection,
path,
index)
await config_bag.context.cfgimpl_reset_cache(option_bag)
#____________________________________________________________
# validate properties
@ -664,29 +688,36 @@ class Settings(object):
option_properties):
raises_properties = context_properties - SPECIAL_PROPERTIES
# remove global permissive properties
if raises_properties and ('permissive' in raises_properties):
if raises_properties and 'permissive' in raises_properties:
raises_properties -= context_permissives
properties = option_properties & raises_properties
# at this point an option should not remain in properties
# at this point it should not remain any property for the option
return properties
async def validate_properties(self,
option_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
"""
config_bag = option_bag.config_bag
if not config_bag.properties or config_bag.properties == frozenset(['cache']): # pragma: no cover
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
return
properties = await self.calc_raises_properties(option_bag)
if properties != frozenset():
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
raise PropertiesOptionError(option_bag,
properties,
self)
self,
help_properties=calc_properties)
def validate_mandatory(self,
value,
@ -731,8 +762,9 @@ class Settings(object):
async def _read(self,
remove,
append,
context):
props = await self._p_.getproperties(None,
config_bag):
props = await self._p_.getproperties(config_bag.connection,
None,
None,
self.default_properties)
modified = False
@ -743,19 +775,20 @@ class Settings(object):
props = props | append
modified = True
if modified:
await self.set_context_properties(frozenset(props),
context)
await self.set_context_properties(config_bag.connection,
frozenset(props),
config_bag.context)
async def read_only(self,
context):
config_bag):
"convenience method to freeze, hide and disable"
await self._read(self.ro_remove,
self.ro_append,
context)
config_bag)
async def read_write(self,
context):
config_bag):
"convenience method to freeze, hide and disable"
await self._read(self.rw_remove,
self.rw_append,
context)
config_bag)

View File

@ -1,4 +1,4 @@
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -18,7 +18,6 @@
settings changes will be lost.
The storage is the system Tiramisu uses to communicate with various DB.
You can specified a persistent storage.
Storage is basic components used to set Config informations in DB.
"""
@ -44,16 +43,26 @@ class Storage:
after.
"""
def __init__(self,
**kwargs: Dict[str, str]) -> None:
self.storage_type = None
engine=None) -> None:
self.storage_type = engine
self.mod = None
if kwargs:
self.setting(**kwargs)
self.kwargs = {}
def get(self):
def engine(self,
engine) -> None:
if self.mod is not None:
raise ValueError(_('cannot change setting when storage is already in use'))
self.storage_type = engine
def setting(self,
**kwargs: Dict[str, str]) -> None:
if self.mod is not None:
raise ValueError(_('cannot change setting when storage is already in use'))
self.kwargs = kwargs
async def get(self):
if self.storage_type is None:
self.storage_type = environ.get('TIRAMISU_STORAGE', DEFAULT_STORAGE)
self.setting()
if self.mod is None:
modulepath = '{0}.storage.{1}'.format(MODULE_PATH,
self.storage_type)
@ -65,25 +74,11 @@ class Storage:
for token in modulepath.split(".")[1:]:
mod = getattr(mod, token)
self.mod = mod
return self.mod
def setting(self,
**kwargs: Dict[str, str]) -> None:
if 'engine' in kwargs:
name = kwargs['engine']
if self.storage_type is not None and self.storage_type != name: # pragma: no cover
raise ConfigError(_('storage_type is already set, '
'cannot rebind it'))
self.storage_type = name
del kwargs['engine']
if kwargs: # pragma: no cover
mod = self.get()
for key, value in kwargs.items():
for key, value in self.kwargs.items():
setattr(mod.SETTING, key, value)
def is_persistent(self):
mod = self.get()
return mod.PERSISTENT
del self.kwargs
await self.mod.init()
return self.mod
default_storage = Storage()
@ -94,60 +89,62 @@ def gen_storage_id(session_id,
config):
if session_id is not None:
return session_id
return 'c' + str(id(config)) + str(int(time())) + str(randint(0, 500))
return 'c' + str(id(config)) + str(int(time())) + str(randint(0, 50000))
async def get_storages(context,
session_id,
persistent,
storage):
delete_old_session,
storage,
connection):
session_id = gen_storage_id(session_id,
context)
if storage is None:
storage = default_storage
imp = storage.get()
imp_storage = await imp.Storage(session_id,
persistent)
imp = await storage.get()
imp_storage = await imp.Storage(connection,
session_id,
delete_old_session)
properties = imp.Properties(imp_storage)
permissives = imp.Permissives(imp_storage)
values = imp.Values(imp_storage)
return properties, permissives, values, session_id
return storage, properties, permissives, values, session_id
async def get_default_values_storages():
imp = memory_storage.get()
storage = await imp.Storage('__validator_storage',
persistent=False,
test=True)
async def get_default_values_storages(connection):
imp = await memory_storage.get()
storage = await imp.Storage(connection,
'__validator_storage',
delete_old_session=True)
return imp.Values(storage)
async def get_default_settings_storages():
imp = memory_storage.get()
storage = await imp.Storage('__validator_storage', persistent=False, test=True)
async def get_default_settings_storages(connection):
imp = await memory_storage.get()
storage = await imp.Storage(connection,
'__validator_storage',
delete_old_session=True)
properties = imp.Properties(storage)
permissives = imp.Permissives(storage)
return properties, permissives
def list_sessions(storage=default_storage):
"""List all available session (persistent or not persistent)
async def list_sessions(storage=default_storage):
"""List all available session
"""
return storage.get().list_sessions()
stor = await storage.get()
return await stor.list_sessions()
def delete_session(session_id, storage=default_storage):
async def delete_session(session_id,
storage=default_storage):
"""Delete a selected session, be careful, you can deleted a session
use by an other instance
:params session_id: id of session to delete
"""
storage_module = storage.get()
session = storage_module.storage.getsession()
storage_module.value.delete_session(session_id)
storage_module.storage.delete_session(session_id)
if session: # pragma: no cover
session.commit()
del(session)
storage_module = await storage.get()
await storage_module.value.delete_session(session_id)
await storage_module.storage.delete_session(session_id)
__all__ = ('list_sessions', 'delete_session')

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2018-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2018-2020 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

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"cache used by storage"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -30,11 +30,11 @@ class Cache(DictCache):
if follower, add index
"""
if 'cache' in props or 'cache' in self_props:
log.debug('setcache %s with index %s and value %s in %s (%s)',
path, index, val, _display_classname(self), id(self))
# log.debug('setcache %s with index %s and value %s in %s (%s)',
# path, index, val, _display_classname(self), id(self))
super().setcache(path, index, val, time(), validated)
log.debug('not setcache %s with index %s and value %s and props %s and %s in %s (%s)',
path, index, val, props, self_props, _display_classname(self), id(self))
# log.debug('not setcache %s with index %s and value %s and props %s and %s in %s (%s)',
# path, index, val, props, self_props, _display_classname(self), id(self))
def getcache(self,
path,
@ -63,31 +63,31 @@ class Cache(DictCache):
'expire' in self_props):
ntime = int(time())
if timestamp + expiration_time >= ntime:
log.debug('getcache in cache (1) %s %s %s %s %s', path, value, _display_classname(self),
id(self), index)
# log.debug('getcache in cache (1) %s %s %s %s %s', path, value, _display_classname(self),
# id(self), index)
return True, value, validated
else:
log.debug('getcache expired value for path %s < %s',
timestamp + expiration_time, ntime)
# else:
# log.debug('getcache expired value for path %s < %s',
# timestamp + expiration_time, ntime)
# if expired, remove from cache
# self.delcache(path)
else:
log.debug('getcache in cache (2) %s %s %s %s %s', path, value, _display_classname(self),
id(self), index)
# log.debug('getcache in cache (2) %s %s %s %s %s', path, value, _display_classname(self),
# id(self), index)
return True, value, validated
log.debug('getcache %s with index %s not in %s cache',
path, index, _display_classname(self))
# log.debug('getcache %s with index %s not in %s cache',
# path, index, _display_classname(self))
return no_cache
def delcache(self, path):
"""remove cache for a specified path
"""
log.debug('delcache %s %s %s', path, _display_classname(self), id(self))
# log.debug('delcache %s %s %s', path, _display_classname(self), id(self))
super().delcache(path)
def reset_all_cache(self):
"empty the cache"
log.debug('reset_all_cache %s %s', _display_classname(self), id(self))
# log.debug('reset_all_cache %s %s', _display_classname(self), id(self))
super().reset_all_cache()
def get_cached(self):
@ -96,5 +96,5 @@ class Cache(DictCache):
example: {'path1': {'index1': ('value1', 'time1')}, 'path2': {'index2': ('value2', 'time2', )}}
"""
cache = super().get_cached()
log.debug('get_chached %s for %s (%s)', cache, _display_classname(self), id(self))
# log.debug('get_chached %s for %s (%s)', cache, _display_classname(self), id(self))
return cache

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -17,14 +17,12 @@
"""Default plugin for storage. All informations are store in a simple
dictionary in memory.
You cannot have persistente informations with this kind of storage.
The advantage of this solution is that you can easily create a Config and
use it. But if something goes wrong, you will lost your modifications.
"""
from .value import Values
from .setting import Properties, Permissives
from .storage import PERSISTENT, SETTING, Storage, list_sessions
from .storage import PERSISTENT, SETTING, Storage, list_sessions, init, Connection
__all__ = ('PERSISTENT',
@ -33,4 +31,6 @@ __all__ = ('PERSISTENT',
'Properties',
'Permissives',
'Storage',
'init',
'Connection',
'list_sessions')

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"default plugin for setting: set it in a simple dictionary"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -20,75 +20,108 @@ from ...log import log
class Properties:
__slots__ = ('_properties',
'_storage')
__slots__ = ('_storage',)
def __init__(self, storage):
# properties attribute: the name of a property enables this property
# key is None for global properties
self._properties = {}
self._storage = storage
# properties
async def setproperties(self, path, index, properties):
async def setproperties(self,
connection,
path,
index,
properties):
log.debug('setproperties %s %s %s', path, index, properties)
self._properties.setdefault(path, {})[index] = properties
self._storage.get_properties().setdefault(path, {})[index] = properties
async def getproperties(self, path, index, default_properties):
if path not in self._properties:
async def getproperties(self,
connection,
path,
index,
default_properties):
properties = self._storage.get_properties()
if path not in properties:
ret = frozenset(default_properties)
else:
ret = self._properties[path].get(index, frozenset(default_properties))
ret = properties[path].get(index, frozenset(default_properties))
log.debug('getproperties %s %s %s', path, index, ret)
return ret
async def delproperties(self, path, index):
async def delproperties(self,
connection,
path,
index):
log.debug('delproperties %s', path)
if path in self._properties and index in self._properties[path]:
del(self._properties[path][index])
properties = self._storage.get_properties()
if path in properties and index in properties[path]:
del(properties[path][index])
async def exportation(self):
async def exportation(self,
connection):
"""return all modified settings in a dictionary
example: {'path1': set(['prop1', 'prop2'])}
"""
return deepcopy(self._properties)
return deepcopy(self._storage.get_properties())
async def importation(self, properties):
self._properties = properties
async def importation(self,
connection,
properties):
self._storage.set_properties(properties)
def getconnection(self):
return self._storage.getconnection()
class Permissives:
__slots__ = ('_permissives',
'_storage')
__slots__ = ('_storage',)
def __init__(self, storage):
# permissive properties
self._permissives = {}
self._storage = storage
async def setpermissives(self, path, index, permissives):
async def setpermissives(self,
connection,
path,
index,
permissives):
log.debug('setpermissives %s %s', path, permissives)
self._permissives.setdefault(path, {})[index] = permissives
self._storage.get_permissives().setdefault(path, {})[index] = permissives
async def getpermissives(self, path, index):
if not path in self._permissives:
async def getpermissives(self,
connection,
path,
index):
permissives = self._storage.get_permissives()
if not path in permissives:
ret = frozenset()
else:
ret = self._permissives[path].get(index, frozenset())
ret = permissives[path].get(index, frozenset())
log.debug('getpermissives %s %s', path, ret)
return ret
async def delpermissive(self, path, index):
async def delpermissive(self,
connection,
path,
index):
log.debug('delpermissive %s', path)
if path in self._permissives and index in self._permissives[path]:
del(self._permissives[path][index])
permissives = self._storage.get_permissives()
if path in permissives and index in permissives[path]:
del(permissives[path][index])
async def exportation(self):
async def exportation(self,
connection):
"""return all modified permissives in a dictionary
example: {'path1': set(['perm1', 'perm2'])}
"""
return deepcopy(self._permissives)
return deepcopy(self._storage.get_permissives())
async def importation(self, permissives):
self._permissives = permissives
async def importation(self,
connection,
permissives):
self._storage.set_permissives(permissives)
def getconnection(self):
return self._storage.getconnection()

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -26,36 +26,95 @@ class Setting:
SETTING = Setting()
_list_sessions = []
PERSISTENT = False
_list_sessions = {}
PERSISTENT = True
def list_sessions():
return _list_sessions
async def init():
pass
class Connection:
async def __aenter__(self):
return self
async def __aexit__(self,
type,
value,
traceback):
pass
async def list_sessions():
lst = list(_list_sessions.keys())
if '__validator_storage' in lst:
lst.remove('__validator_storage')
return lst
async def delete_session(session_id):
try:
del _list_sessions[session_id]
except KeyError:
pass
@asyncinit
class Storage:
__slots__ = ('session_id', 'persistent')
__slots__ = ('session_id',)
storage = 'dictionary'
# if object could be serializable
serializable = True
async def __init__(self, session_id, persistent, test=False):
if not test and session_id in _list_sessions:
raise ConflictError(_('session "{}" already exists').format(session_id))
if persistent:
raise ValueError(_('a dictionary cannot be persistent'))
def add_session(self):
# values (('path1',), (index1,), (value1,), ('owner1'))
_list_sessions[self.session_id] = {'values': ([], [], [], []),
'informations': {},
'properties': {},
'permissives': {}}
async def __init__(self,
connection: Connection,
session_id: str,
delete_old_session: bool) -> None:
if not isinstance(session_id, str):
raise ValueError(_('session_id has to be a string'))
self.session_id = session_id
self.persistent = persistent
_list_sessions.append(self.session_id)
if self.session_id not in _list_sessions:
self.add_session()
def __del__(self):
try:
_list_sessions.remove(self.session_id)
except AttributeError:
pass
async def delete_session(self):
await delete_session(self.session_id)
async def list_sessions(self):
return await list_sessions()
def getsession():
pass
def get_session(self):
if self.session_id not in _list_sessions:
self.add_session()
return _list_sessions.get(self.session_id, {})
def get_values(self):
return self.get_session()['values']
def set_values(self, values):
self.get_session()['values'] = values
def get_informations(self):
return self.get_session()['informations']
def set_informations(self, informations):
self.get_session()['informations'] = informations
def get_properties(self):
return self.get_session()['properties']
def set_properties(self, properties):
self.get_session()['properties'] = properties
def get_permissives(self):
return self.get_session()['permissives']
def set_permissives(self, permissives):
self.get_session()['permissives'] = permissives
def getconnection(self):
return Connection()

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"default plugin for value: set it in a simple dictionary"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -18,6 +18,7 @@
from ...setting import undefined
from ...i18n import _
from ...log import log
from .storage import delete_session
from copy import deepcopy
@ -30,16 +31,12 @@ class Values:
def __init__(self, storage):
"""init plugin means create values storage
"""
#(('path1',), (index1,), (value1,), ('owner1'))
self._values = ([], [], [], [])
self._informations = {}
#self._values = ([], [], [], [])
#self._informations = {}
self._storage = storage
async def commit(self):
pass
def _setvalue_info(self, nb, idx, value, index, follower_idx=None):
lst = self._values[nb]
lst = self._storage.get_values()[nb]
if index is None or nb == 0:
# not follower or path
lst[idx] = value
@ -58,26 +55,27 @@ class Values:
def _add_new_value(self, index, nb, value):
if index is None or nb == 0:
# not follower or path
self._values[nb].append(value)
self._storage.get_values()[nb].append(value)
else:
# follower
self._values[nb].append([value])
self._storage.get_values()[nb].append([value])
def _add_new_value(self, index, nb, value):
if index is None or nb == 0:
# not follower or path
self._values[nb].append(value)
self._storage.get_values()[nb].append(value)
else:
# follower
self._values[nb].append([value])
self._storage.get_values()[nb].append([value])
# value
async def setvalue(self,
connection,
path,
value,
owner,
index,
commit):
new=False):
"""set value for a path
a specified value must be associated to an owner
"""
@ -85,8 +83,9 @@ class Values:
#if isinstance(value, list):
# value = value
if path in self._values[0]:
idx = self._values[0].index(path)
values = self._storage.get_values()
if not new and path in values[0]:
idx = values[0].index(path)
self._setvalue_info(0, idx, path, index)
follower_idx = self._setvalue_info(1, idx, index, index)
self._setvalue_info(2, idx, value, index, follower_idx)
@ -97,47 +96,56 @@ class Values:
self._add_new_value(index, 2, value)
self._add_new_value(index, 3, owner)
async def hasvalue(self, path, index=None):
async def hasvalue(self,
connection,
path,
index=None):
"""if path has a value
return: boolean
"""
has_path = path in self._values[0]
values = self._storage.get_values()
has_path = path in values[0]
log.debug('hasvalue %s %s %s %s', path, index, has_path, id(self))
if index is None:
return has_path
elif has_path:
path_idx = self._values[0].index(path)
indexes = self._values[1][path_idx]
path_idx = values[0].index(path)
indexes = values[1][path_idx]
return index in indexes
return False
async def reduce_index(self, path, index):
async def reduce_index(self,
connection,
path,
index):
"""
_values == ((path1, path2), ((idx1_1, idx1_2), None), ((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2))
"""
log.debug('reduce_index %s %s %s', path, index, id(self))
path_idx = self._values[0].index(path)
values = self._storage.get_values()
path_idx = values[0].index(path)
# get the "index" position
subidx = self._values[1][path_idx].index(index)
subidx = values[1][path_idx].index(index)
# reduce to one the index
self._values[1][path_idx][subidx] -= 1
values[1][path_idx][subidx] -= 1
async def resetvalue_index(self,
connection,
path,
index,
commit):
index):
log.debug('resetvalue_index %s %s %s', path, index, id(self))
values = self._storage.get_values()
def _resetvalue(nb):
del self._values[nb][path_idx]
del self._storage.get_values()[nb][path_idx]
def _resetvalue_index(nb):
del self._values[nb][path_idx][subidx]
del self._storage.get_values()[nb][path_idx][subidx]
path_idx = self._values[0].index(path)
indexes = self._values[1][path_idx]
path_idx = values[0].index(path)
indexes = values[1][path_idx]
if index in indexes:
subidx = indexes.index(index)
if len(self._values[1][path_idx]) == 1:
if len(values[1][path_idx]) == 1:
_resetvalue(0)
_resetvalue(1)
_resetvalue(2)
@ -148,15 +156,16 @@ class Values:
_resetvalue_index(3)
async def resetvalue(self,
path,
commit):
connection,
path):
"""remove value means delete value in storage
"""
log.debug('resetvalue %s %s', path, id(self))
values = self._storage.get_values()
def _resetvalue(nb):
self._values[nb].pop(idx)
if path in self._values[0]:
idx = self._values[0].index(path)
values[nb].pop(idx)
if path in values[0]:
idx = values[0].index(path)
_resetvalue(0)
_resetvalue(1)
_resetvalue(2)
@ -164,19 +173,22 @@ class Values:
# owner
async def setowner(self,
connection,
path,
owner,
index=None):
index):
"""change owner for a path
"""
idx = self._values[0].index(path)
values = self._storage.get_values()
idx = values[0].index(path)
if index is None:
follower_idx = None
else:
follower_idx = self._values[1][idx].index(index)
follower_idx = values[1][idx].index(index)
self._setvalue_info(3, idx, owner, index, follower_idx)
async def getowner(self,
connection,
path,
default,
index=None,
@ -202,24 +214,25 @@ class Values:
"""
_values == ((path1, path2), ((idx1_1, idx1_2), None), ((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2))
"""
values = self._storage.get_values()
value = undefined
if path in self._values[0]:
path_idx = self._values[0].index(path)
indexes = self._values[1][path_idx]
if path in values[0]:
path_idx = values[0].index(path)
indexes = values[1][path_idx]
if indexes is None:
if index is not None: # pragma: no cover
raise ValueError('index is forbidden for {}'.format(path))
owner = self._values[3][path_idx]
owner = values[3][path_idx]
if with_value:
value = self._values[2][path_idx]
value = values[2][path_idx]
else:
if index is None: # pragma: no cover
raise ValueError('index is mandatory for {}'.format(path))
if index in indexes:
subidx = indexes.index(index)
owner = self._values[3][path_idx][subidx]
owner = values[3][path_idx][subidx]
if with_value:
value = self._values[2][path_idx][subidx]
value = values[2][path_idx][subidx]
else:
owner = undefined
else:
@ -228,57 +241,79 @@ class Values:
value = list(value)
return owner, value
async def set_information(self, path, key, value):
async def set_information(self,
connection,
path,
key,
value):
"""updates the information's attribute
(which is a dictionary)
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
self._informations.setdefault(path, {})
self._informations[path][key] = value
informations = self._storage.get_informations()
informations.setdefault(path, {})
informations[path][key] = value
async def get_information(self, path, key, default):
async def get_information(self,
connection,
path,
key,
default):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
value = self._informations.get(path, {}).get(key, default)
value = self._storage.get_informations().get(path, {}).get(key, default)
if value is undefined:
raise ValueError(_("information's item"
" not found: {0}").format(key))
return value
async def del_information(self, path, key, raises):
if path in self._informations and key in self._informations[path]:
del self._informations[path][key]
async def del_information(self,
connection,
path,
key,
raises):
informations = self._storage.get_informations()
if path in informations and key in informations[path]:
del informations[path][key]
else:
if raises:
raise ValueError(_("information's item not found {0}").format(key))
async def list_information(self, path):
if path in self._informations:
return self._informations[path].keys()
async def list_information(self,
connection,
path):
informations = self._storage.get_informations()
if path in informations:
return informations[path].keys()
else:
return []
async def del_informations(self):
self._informations = {}
async def del_informations(self,
connection):
self._storage.set_informations({})
async def exportation(self):
return deepcopy(self._values)
async def exportation(self,
connection):
return deepcopy(self._storage.get_values())
async def importation(self, export):
self._values = deepcopy(export)
async def importation(self,
connection,
export):
self._storage.set_values(deepcopy(export))
async def get_max_length(self,
connection,
path):
if path in self._values[0]:
idx = self._values[0].index(path)
values = self._storage.get_values()
if path in values[0]:
idx = values[0].index(path)
else:
return 0
return max(self._values[1][idx]) + 1
return max(values[1][idx]) + 1
def delete_session(session_id):
raise ValueError(_('cannot delete none persistent session'))
def getconnection(self):
return self._storage.getconnection()

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2020 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/>.
# ____________________________________________________________
"""Postgres plugin for storage.
"""
from .value import Values
from .setting import Properties, Permissives
from .storage import PERSISTENT, SETTING, Storage, list_sessions, init, Connection
__all__ = ('PERSISTENT',
'SETTING',
'Values',
'Properties',
'Permissives',
'Storage',
'init',
'Connection',
'list_sessions')

View File

@ -0,0 +1,179 @@
# -*- coding: utf-8 -*-
"default plugin for setting: set it in a simple dictionary"
# Copyright (C) 2020 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/>.
# ____________________________________________________________
try:
from cPickle import loads, dumps
except ImportError:
from pickle import loads, dumps
from ...log import log
class Properties:
__slots__ = ('_storage',)
def __init__(self, storage):
self._storage = storage
# properties
async def setproperties(self,
connection,
path,
index,
properties):
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
await self.delproperties(connection, path, index)
sql = "INSERT INTO property(path, idx, properties, session_id) VALUES ($1, $2, $3, $4)"
params = [path, index, dumps(properties), self._storage.database_id]
await connection.execute(sql, *params)
async def getproperties(self,
connection,
path,
index,
default_properties):
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
sql = 'SELECT properties FROM property WHERE path = $1 AND session_id = $2 AND idx = $3'
params = [path, self._storage.database_id, index]
value = await connection.fetchval(sql, *params)
if value is None:
return set(default_properties)
else:
return set(loads(value))
async def delproperties(self,
connection,
path,
index):
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
sql = 'DELETE FROM property WHERE session_id = $1 AND path = $2 AND idx = $3'
params = [self._storage.database_id, path, index]
await connection.execute(sql, *params)
async def exportation(self,
connection):
"""return all modified settings in a dictionary
example: {'path1': set(['prop1', 'prop2'])}
"""
ret = {}
for path, idx, properties, _ in await connection.fetch("SELECT * FROM property "
"WHERE session_id = $1",
self._storage.database_id):
idx = self._storage.load_index(idx)
path = self._storage.load_path(path)
ret.setdefault(path, {})[idx] = loads(properties)
return ret
async def importation(self,
connection,
properties):
await connection.execute("DELETE FROM property WHERE session_id = $1", self._storage.database_id)
for path, indexed_properties in properties.items():
path = self._storage.convert_path(path)
for index, property_ in indexed_properties.items():
index = self._storage.convert_index(index)
await connection.execute("INSERT INTO property(path, idx, properties, session_id) "
"VALUES ($1,$2,$3,$4)", path,
index,
dumps(property_),
self._storage.database_id)
def getconnection(self):
return self._storage.getconnection()
class Permissives:
__slots__ = ('_storage',)
def __init__(self,
storage):
self._storage = storage
# permissive
async def setpermissives(self,
connection,
path,
index,
permissive):
# log.debug('setpermissive %s %s %s %s', path, index, permissive, id(self))
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
await self.delpermissive(connection,
path,
index)
await connection.execute("INSERT INTO permissive(path, idx, permissives, session_id) "
"VALUES ($1,$2,$3,$4)", path,
index,
dumps(permissive),
self._storage.database_id)
async def getpermissives(self,
connection,
path,
index):
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
sql = 'SELECT permissives FROM permissive WHERE session_id = $1 AND path = $2 AND idx = $3'
params = [self._storage.database_id, path, index]
permissives = await connection.fetchval(sql,
*params)
if permissives is None:
return frozenset()
else:
return loads(permissives)
# log.debug('getpermissive %s %s %s', path, ret, id(self))
async def delpermissive(self,
connection,
path,
index):
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
sql = 'DELETE FROM permissive WHERE session_id = $1 AND path = $2 AND idx = $3'
params = [self._storage.database_id, path, index]
await connection.execute(sql, *params)
async def exportation(self,
connection):
"""return all modified permissives in a dictionary
example: {'path1': set(['perm1', 'perm2'])}
"""
ret = {}
sql = "SELECT path, idx, permissives FROM permissive WHERE session_id = $1"
for path, index, permissives in await connection.fetch(sql,
self._storage.database_id):
ret.setdefault(self._storage.load_path(path), {})[self._storage.load_index(index)] = loads(permissives)
return ret
async def importation(self,
connection,
permissives):
await connection.execute("DELETE FROM permissive WHERE session_id = $1", self._storage.database_id)
for path, indexed_permissives in permissives.items():
for index, permissive in indexed_permissives.items():
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
await connection.execute("INSERT INTO permissive(path, idx, permissives, session_id) "
"VALUES ($1,$2,$3,$4)", path,
index,
dumps(permissive),
self._storage.database_id)
def getconnection(self):
return self._storage.getconnection()

View File

@ -0,0 +1,183 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2020 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 asyncpg import create_pool
from asyncpg.exceptions import UniqueViolationError
import warnings
from os.path import join
from typing import Optional, Dict
from ...i18n import _
from ...error import ConflictError
from ...asyncinit import asyncinit
class Setting:
""":param dsn: something like postgres://tiramisu:tiramisu@localhost:5432/tiramisu
"""
__slots__ = ('dsn',)
def __init__(self):
self.dsn = 'postgres://tiramisu:tiramisu@localhost:5432/tiramisu'
# FIXME
self.dsn = 'postgres:///tiramisu?host=/var/run/postgresql/&user=tiramisu'
def __setattr__(self, key, value):
if POOL is not None: # pragma: no cover
raise Exception(_('cannot change setting when connexion is already '
'opened'))
super().__setattr__(key, value)
POOL = None
PERSISTENT = True
SETTING = Setting()
class Connection:
async def __aenter__(self):
self.connection = await POOL.acquire()
self.transaction = self.connection.transaction()
await self.transaction.__aenter__()
return self
async def __aexit__(self,
type,
value,
traceback):
await self.transaction.__aexit__(type,
value,
traceback)
await self.connection.close()
async def fetch(self,
*args):
return await self.connection.fetch(*args)
async def fetchrow(self,
*args):
return await self.connection.fetchrow(*args)
async def fetchval(self,
*args):
return await self.connection.fetchval(*args)
async def execute(self,
*args):
await self.connection.execute(*args)
async def list_sessions():
async with Connection() as connection:
return await _list_sessions(connection)
async def _list_sessions(connection):
return [row[0] for row in await connection.fetch("SELECT session FROM session")]
async def delete_session(session_id):
async with Connection() as connection:
database_id = await connection.fetchval("SELECT session_id FROM session WHERE session = $1", session_id)
if database_id is not None:
await _delete_session(database_id,
connection)
async def _delete_session(database_id,
connection):
await connection.execute('DELETE FROM property WHERE session_id = $1', database_id)
await connection.execute('DELETE FROM permissive WHERE session_id = $1', database_id)
await connection.execute('DELETE FROM value WHERE session_id = $1', database_id)
await connection.execute('DELETE FROM information WHERE session_id = $1', database_id)
await connection.execute('DELETE FROM session WHERE session_id = $1', database_id)
async def init():
# self.pool = await connect(dsn=SETTING.dsn)
global POOL
if POOL is None:
POOL = await create_pool(dsn=SETTING.dsn)
#print(' async with POOL.acquire() as connection:')
#print(' async with connection.transaction():')
sql = """
CREATE TABLE IF NOT EXISTS session(session_id SERIAL, session TEXT UNIQUE, PRIMARY KEY(session_id));
CREATE TABLE IF NOT EXISTS property(path TEXT, idx INTEGER, properties BYTEA, session_id INTEGER, PRIMARY KEY(path, idx, session_id), FOREIGN KEY(session_id) REFERENCES session(session_id));
CREATE TABLE IF NOT EXISTS permissive(path TEXT, idx INTEGER, permissives BYTEA, session_id INTEGER, PRIMARY KEY(path, idx, session_id), FOREIGN KEY(session_id) REFERENCES session(session_id));
CREATE TABLE IF NOT EXISTS value(path TEXT, value BYTEA, owner TEXT, idx INTEGER, session_id INTEGER, PRIMARY KEY (path, idx, session_id), FOREIGN KEY(session_id) REFERENCES session(session_id));
CREATE TABLE IF NOT EXISTS information(key TEXT, value BYTEA, session_id INTEGER, path TEXT, PRIMARY KEY (key, session_id), FOREIGN KEY(session_id) REFERENCES session(session_id));"""
#print(' await connection.execute("""'+sql+'""")')
await POOL.execute(sql)
@asyncinit
class Storage:
__slots__ = ('pool',
'database_id',
'session_id',
'created')
storage = 'postgres'
async def __init__(self,
connection: Connection,
session_id: str,
delete_old_session: bool) -> None:
if not isinstance(session_id, str):
raise ValueError(_('session_id has to be a string'))
self.database_id = None
self.session_id = session_id
select = await connection.fetchval("SELECT session_id FROM session WHERE session = $1",
self.session_id)
if select is not None:
if delete_old_session:
await self.delete_session()
else:
self.database_id = select
if self.database_id is None:
self.database_id = await connection.fetchval('INSERT INTO session(session) VALUES ($1) RETURNING session_id',
self.session_id)
def convert_index(self, index):
if index is None:
index = -1
return index
def convert_path(self, path):
if path is None:
path = '_none'
return path
def load_index(self, index):
if index == -1:
index = None
return index
def load_path(self, path):
if path == '_none':
path = None
return path
async def delete_session(self):
if self.database_id is not None:
await _delete_session(self.database_id,
POOL)
self.database_id = None
async def list_sessions(self,
connection):
return await _list_sessions(connection)
def getconnection(self):
return Connection()

View File

@ -0,0 +1,298 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2020 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/>.
# ____________________________________________________________
try:
from cPickle import loads, dumps
except ImportError:
from pickle import loads, dumps
from ...setting import undefined, owners
from ...i18n import _
from ...log import log
from .storage import delete_session
class Values:
__slots__ = ('__weakref__',
'_storage')
def __init__(self,
storage):
"""init plugin means create values storage
"""
self._storage = storage
# value
async def setvalue(self,
connection,
path,
value,
owner,
index,
new=False):
"""set value for an option
a specified value must be associated to an owner
"""
# log.debug('setvalue %s %s %s %s %s', path, value, owner, index, commit)
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
sql = 'INSERT INTO value(value, owner, session_id, path, idx) VALUES ($1,$2,$3,$4,$5)'
idx = self._storage.convert_index(index)
path = self._storage.convert_path(path)
params = [dumps(value), str(owner), self._storage.database_id, path, idx]
if new is False:
if index != -1:
await self.resetvalue_index(connection,
path,
index)
else:
await self.resetvalue(connection,
path)
await connection.execute(sql,
*params)
async def hasvalue(self,
connection,
path,
index=None):
"""if opt has a value
return: boolean
"""
# log.debug('hasvalue %s %s', path, index)
if index is not None:
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
request = "SELECT value FROM value WHERE path = $1 AND session_id = $2 AND idx = $3"
params = (path, self._storage.database_id, index)
else:
path = self._storage.convert_path(path)
request = "SELECT value FROM value WHERE path = $1 AND session_id = $2"
params = (path, self._storage.database_id)
ret = await connection.fetchrow(request, *params)
return ret is not None
async def reduce_index(self,
connection,
path,
index):
"""
_values == ((path1, path2), ((idx1_1, idx1_2), None),
((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2))
"""
# log.debug('reduce_index %s %s %s', path, index, id(self))
await connection.execute("UPDATE value SET idx = $1 WHERE path = $2 and idx = $3 "
"AND session_id = $4",
index - 1, path, index, self._storage.database_id)
async def resetvalue_index(self,
connection,
path,
index):
"""remove value means delete value in storage
"""
# log.debug('resetvalue_index %s %s', path, index)
await connection.execute("DELETE FROM value WHERE path = $1 AND session_id = $2 AND idx = $3",
path, self._storage.database_id, index)
await self.hasvalue(connection,
path,
index)
async def resetvalue(self,
connection,
path):
"""remove value means delete value in storage
"""
# log.debug('resetvalue %s', path)
await connection.execute("DELETE FROM value WHERE path = $1 AND session_id = $2",
path, self._storage.database_id)
# owner
async def setowner(self,
connection,
path,
owner,
index):
"""change owner for an option
"""
# log.debug('setowner %s %s %s', path, owner, index)
index = self._storage.convert_index(index)
path = self._storage.convert_path(path)
await connection.execute("UPDATE value SET owner = $1 WHERE path = $2 and idx = $3 AND session_id = $4",
str(owner), path, index, self._storage.database_id)
async def getowner(self,
connection,
path,
default,
index,
with_value=False):
"""get owner for an option
return: owner object
"""
# log.debug('getowner %s %s %s %s', path, default, index, with_value)
path = self._storage.convert_path(path)
index = self._storage.convert_index(index)
request = "SELECT owner, value FROM value WHERE session_id = $1 AND path = $2 AND idx = $3"
params = [self._storage.database_id, path, index]
owner = await connection.fetchrow(request, *params)
if owner is None:
if not with_value:
return default
return default, None
# autocreate owners
try:
nowner = getattr(owners, owner[0])
except AttributeError: # pragma: no cover
owners.addowner(owner[0])
nowner = getattr(owners, owner[0])
if not with_value:
return nowner
value = loads(owner[1])
return nowner, value
async def set_information(self,
connection,
path,
key,
value):
"""updates the information's attribute
(which is a dictionary)
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
# log.debug('set_information %s %s', key, value)
path = self._storage.convert_path(path)
await connection.execute("DELETE FROM information WHERE key = $1 AND session_id = $2 AND path = $3",
key, self._storage.database_id, path)
await connection.execute("INSERT INTO information(key, value, session_id, path) VALUES "
"($1, $2, $3, $4)", key, dumps(value), self._storage.database_id, path)
async def get_information(self,
connection,
path,
key,
default):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
# log.debug('get_information %s %s', key, default)
path = self._storage.convert_path(path)
value = await connection.fetchval("SELECT value FROM information WHERE key = $1 AND "
"session_id = $2 AND path = $3",
key, self._storage.database_id, path)
if value is None:
if default is undefined:
raise ValueError(_("information's item"
" not found: {0}").format(key))
return default
else:
return loads(value)
async def del_information(self,
connection,
path,
key,
raises):
# log.debug('del_information %s %s', key, raises)
path = self._storage.convert_path(path)
information = await connection.fetchval("SELECT value FROM information WHERE key = $1 "
"AND session_id = $2 AND path = $3",
key, self._storage.database_id, path)
if raises and information is None:
raise ValueError(_("information's item not found {0}").format(key))
await connection.execute("DELETE FROM information WHERE key = $1 AND session_id = $2 AND path = $3",
key, self._storage.database_id, path)
async def list_information(self,
connection,
path):
path = self._storage.convert_path(path)
return [row[0] for row in await connection.fetch("SELECT key FROM information WHERE session_id = $1 AND path = $2",
self._storage.database_id, path)]
async def del_informations(self,
connection):
await connection.execute("DELETE FROM information WHERE session_id = $1",
self._storage.database_id)
async def exportation(self,
connection):
# log.debug('exportation')
ret = [[], [], [], []]
rows = await connection.fetch("SELECT path, value, owner, idx FROM value WHERE "
"session_id = $1", self._storage.database_id)
for row in rows:
path = self._storage.load_path(row[0])
value = loads(row[1])
owner = row[2]
index = self._storage.load_index(row[3])
if index is None:
ret[0].append(path)
ret[1].append(index)
ret[2].append(value)
ret[3].append(owner)
else:
if path in ret[0]:
path_idx = ret[0].index(path)
ret[1][path_idx].append(index)
ret[2][path_idx].append(value)
ret[3][path_idx].append(owner)
else:
ret[0].append(path)
ret[1].append([index])
ret[2].append([value])
ret[3].append([owner])
return ret
async def importation(self,
connection,
export):
# log.debug('importation')
request = "DELETE FROM value WHERE session_id = $1"
await connection.execute(request, self._storage.database_id)
for idx, path in enumerate(export[0]):
path = self._storage.convert_path(path)
index = self._storage.convert_index(export[1][idx])
value = export[2][idx]
owner = export[3][idx]
if index == -1:
await connection.execute("INSERT INTO value(path, value, owner, idx, session_id) VALUES "
"($1, $2, $3, $4, $5)", path, dumps(value),
str(owner), index,
self._storage.database_id)
else:
for val in zip(index, value, owner):
await connection.execute("INSERT INTO value(path, value, owner, idx, session_id)"
"VALUES ($1, $2, $3, $4, $5)", path,
dumps(val[1]),
str(val[2]), val[0],
self._storage.database_id)
async def get_max_length(self,
connection,
path):
# log.debug('get_max_length %s', path)
val_max = await connection.fetchval("SELECT max(idx) FROM value WHERE path = $1 AND session_id = $2",
path, self._storage.database_id)
if val_max is None:
return 0
return val_max + 1
def getconnection(self):
return self._storage.getconnection()

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -14,15 +14,11 @@
# 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/>.
# ____________________________________________________________
"""Sqlite3 plugin for storage. This storage is not made to be used in productive
environment. It was developing as proof of concept.
You should not configure differents Configs with same session_id.
"""Sqlite3 plugin for storage.
"""
from .value import Values
from .setting import Properties, Permissives
from .storage import PERSISTENT, SETTING, Storage, list_sessions
from .storage import PERSISTENT, SETTING, Storage, list_sessions, init, Connection
__all__ = ('PERSISTENT',
@ -31,4 +27,6 @@ __all__ = ('PERSISTENT',
'Properties',
'Permissives',
'Storage',
'init',
'Connection',
'list_sessions')

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"default plugin for setting: set it in a simple dictionary"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -26,15 +26,25 @@ class Properties(Sqlite3DB):
super(Properties, self).__init__(storage)
# properties
async def setproperties(self, path, index, properties):
await self.delproperties(path, index, commit=False)
await self._storage.execute("INSERT INTO property(path, tiram_index, properties, session_id) VALUES "
"(?, ?, ?, ?)", (path,
index,
self._sqlite_encode(properties),
self._session_id))
async def setproperties(self,
connection,
path,
index,
properties):
await self.delproperties(connection,
path,
index)
await connection.execute("INSERT INTO property(path, tiram_index, properties, session_id) VALUES "
"(?, ?, ?, ?)", (path,
index,
self._sqlite_encode(properties),
self._session_id))
async def getproperties(self, path, index, default_properties):
async def getproperties(self,
connection,
path,
index,
default_properties):
sql = 'SELECT properties FROM property WHERE session_id = ? '
params = [self._session_id]
if path is None:
@ -47,13 +57,16 @@ class Properties(Sqlite3DB):
else:
sql += "AND tiram_index = ? LIMIT 1"
params.append(index)
value = await self._storage.select(sql, params)
value = await connection.select(sql, params)
if value is None:
return set(default_properties)
else:
return set(self._sqlite_decode(value[0]))
async def delproperties(self, path, index, commit=True):
async def delproperties(self,
connection,
path,
index):
sql = 'DELETE FROM property WHERE session_id = ? '
params = [self._session_id]
if path is None:
@ -66,47 +79,61 @@ class Properties(Sqlite3DB):
else:
params.append(index)
sql += 'AND tiram_index = ?'
await self._storage.execute(sql, params, commit)
await connection.execute(sql, params)
async def exportation(self):
async def exportation(self,
connection):
"""return all modified settings in a dictionary
example: {'path1': set(['prop1', 'prop2'])}
"""
ret = {}
for path, tiram_index, properties, _ in await self._storage.select("SELECT * FROM property "
"WHERE session_id = ?",
(self._session_id,),
only_one=False):
for path, tiram_index, properties, _ in await connection.select("SELECT * FROM property "
"WHERE session_id = ?",
(self._session_id,),
only_one=False):
ret.setdefault(path, {})[tiram_index] = self._sqlite_decode(properties)
return ret
async def importation(self, properties):
await self._storage.execute("DELETE FROM property WHERE session_id = ?", (self._session_id,), commit=False)
async def importation(self,
connection,
properties):
await connection.execute("DELETE FROM property WHERE session_id = ?", (self._session_id,))
for path, indexed_properties in properties.items():
for index, property_ in indexed_properties.items():
await self._storage.execute("INSERT INTO property(path, tiram_index, properties, session_id) "
"VALUES (?, ?, ?, ?)", (path,
index,
self._sqlite_encode(property_),
self._session_id,
), False)
self._storage._conn.commit()
await connection.execute("INSERT INTO property(path, tiram_index, properties, session_id) "
"VALUES (?, ?, ?, ?)", (path,
index,
self._sqlite_encode(property_),
self._session_id,
))
def getconnection(self):
return self._storage.getconnection()
class Permissives(Sqlite3DB):
__slots__ = tuple()
# permissive
async def setpermissives(self, path, index, permissive):
async def setpermissives(self,
connection,
path,
index,
permissive):
log.debug('setpermissive %s %s %s %s', path, index, permissive, id(self))
await self.delpermissive(path, index, commit=False)
await self._storage.execute("INSERT INTO permissive(path, tiram_index, permissives, session_id) "
"VALUES (?, ?, ?, ?)", (path,
index,
self._sqlite_encode(permissive),
self._session_id))
await self.delpermissive(connection,
path,
index)
await connection.execute("INSERT INTO permissive(path, tiram_index, permissives, session_id) "
"VALUES (?, ?, ?, ?)", (path,
index,
self._sqlite_encode(permissive),
self._session_id))
async def getpermissives(self, path, index):
async def getpermissives(self,
connection,
path,
index):
sql = 'SELECT permissives FROM permissive WHERE session_id = ? '
params = [self._session_id]
if path is None:
@ -119,7 +146,7 @@ class Permissives(Sqlite3DB):
else:
sql += "AND tiram_index = ? LIMIT 1"
params.append(index)
permissives = await self._storage.select(sql, params)
permissives = await connection.select(sql, params)
if permissives is None:
ret = frozenset()
else:
@ -127,7 +154,10 @@ class Permissives(Sqlite3DB):
log.debug('getpermissive %s %s %s', path, ret, id(self))
return ret
async def delpermissive(self, path, index, commit=True):
async def delpermissive(self,
connection,
path,
index):
sql = 'DELETE FROM permissive WHERE session_id = ? '
params = [self._session_id]
if path is None:
@ -140,29 +170,30 @@ class Permissives(Sqlite3DB):
else:
params.append(index)
sql += 'AND tiram_index = ?'
await self._storage.execute(sql, params, commit)
await connection.execute(sql, params)
async def exportation(self):
async def exportation(self,
connection):
"""return all modified permissives in a dictionary
example: {'path1': set(['perm1', 'perm2'])}
"""
ret = {}
sql = "SELECT path, tiram_index, permissives FROM permissive WHERE session_id = ?"
for path, index, permissives in await self._storage.select(sql,
(self._session_id,),
only_one=False):
for path, index, permissives in await connection.select(sql,
(self._session_id,),
only_one=False):
ret.setdefault(path, {})[index] = self._sqlite_decode(permissives)
return ret
async def importation(self, permissives):
await self._storage.execute("DELETE FROM permissive WHERE session_id = ?", (self._session_id,),
commit=False)
async def importation(self,
connection,
permissives):
await connection.execute("DELETE FROM permissive WHERE session_id = ?", (self._session_id,))
for path, indexed_permissives in permissives.items():
for index, permissive in indexed_permissives.items():
await self._storage.execute("INSERT INTO permissive(path, tiram_index, permissives, session_id) "
"VALUES (?, ?, ?, ?)", (path,
index,
self._sqlite_encode(permissive),
self._session_id,
), False)
self._storage._conn.commit()
await connection.execute("INSERT INTO permissive(path, tiram_index, permissives, session_id) "
"VALUES (?, ?, ?, ?)", (path,
index,
self._sqlite_encode(permissive),
self._session_id,
))

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"sqlite3"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
" with sqlite3 engine"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -29,6 +29,67 @@ global CONN
CONN = None
async def init():
global CONN
if CONN is None:
CONN = sqlite3.connect(_gen_filename())
CONN.text_factory = str
session_table = 'CREATE TABLE IF NOT EXISTS session(session_id INTEGER, session TEXT UNIQUE, PRIMARY KEY(session_id))'
settings_table = 'CREATE TABLE IF NOT EXISTS property(path TEXT, tiram_index INTEGER, properties TEXT, session_id INTEGER, PRIMARY KEY(path, tiram_index, session_id), ' \
'FOREIGN KEY(session_id) REFERENCES session(session_id))'
permissives_table = 'CREATE TABLE IF NOT EXISTS permissive(path TEXT, tiram_index INTEGER, permissives TEXT, session_id INTEGER, ' \
'PRIMARY KEY(path, tiram_index, session_id), ' \
'FOREIGN KEY(session_id) REFERENCES session(session_id))'
values_table = 'CREATE TABLE IF NOT EXISTS value(path TEXT, value TEXT, owner TEXT, idx INTEGER, session_id INTEGER, ' \
'PRIMARY KEY (path, idx, session_id), ' \
'FOREIGN KEY(session_id) REFERENCES session(session_id))'
informations_table = 'CREATE TABLE IF NOT EXISTS information(key TEXT, value TEXT, session_id INTEGER, path TEXT, ' \
'PRIMARY KEY (key, session_id), ' \
'FOREIGN KEY(session_id) REFERENCES session(session_id))'
cursor = CONN.cursor()
cursor.execute(session_table)
cursor.execute(values_table)
cursor.execute(informations_table)
cursor.execute(settings_table)
cursor.execute(permissives_table)
CONN.commit()
class Connection:
async def __aenter__(self):
self.connection = CONN.cursor()
return self
async def __aexit__(self,
type,
value,
traceback):
if type is None:
CONN.commit()
else:
CONN.rollback()
self.connection.close()
async def execute(self,
sql: str,
params: Optional[Dict]=None) -> None:
if params is None:
params = tuple()
self.connection.execute(sql, params)
async def select(self,
sql: str,
params: Optional[Dict]=None,
only_one: bool=True) -> 'Row':
if params is None:
params = tuple()
self.connection.execute(sql, params)
if only_one:
return self.connection.fetchone()
else:
return self.connection.fetchall()
class Setting:
""":param extension: database file extension (by default: db)
:param dir_database: root database directory (by default: /tmp)
@ -57,134 +118,83 @@ def _gen_filename():
return join(SETTING.dir_database, '{0}.{1}'.format(SETTING.name, SETTING.extension))
def list_sessions():
async def list_sessions():
if not CONN:
warnings.warn_explicit(Warning(_('Cannot list sessions, please connect to database first')),
category=Warning,
filename=__file__,
lineno=63)
return []
else:
cursor = CONN.cursor()
names = [row[0] for row in cursor.execute("SELECT session FROM session").fetchall()]
return names
def delete_session(session_id,
_session_id=None):
cursor = CONN.cursor()
if _session_id is None:
_session_id = cursor.execute("SELECT session_id FROM session WHERE session = ?",
(session_id,)).fetchone()
if _session_id is not None:
_session_id = _session_id[0]
if _session_id is not None:
cursor.execute("DELETE FROM property WHERE session_id = ?", (_session_id,))
cursor.execute("DELETE FROM permissive WHERE session_id = ?", (_session_id,))
cursor.execute("DELETE FROM value WHERE session_id = ?", (_session_id,))
cursor.execute("DELETE FROM information WHERE session_id = ?", (_session_id,))
cursor.execute("DELETE FROM session WHERE session_id = ?", (_session_id,))
CONN.commit()
return await _list_sessions(cursor)
async def _list_sessions(cursor):
names = [row[0] for row in cursor.execute("SELECT session FROM session").fetchall()]
return names
async def delete_session(session_id):
cursor = CONN.cursor()
ret = cursor.execute("SELECT session_id FROM session WHERE session = ?",
(session_id,)).fetchone()
if ret is not None:
database_id = ret[0]
await _delete_session(database_id,
cursor)
cursor.close()
async def _delete_session(database_id,
cursor):
cursor.execute("DELETE FROM property WHERE session_id = ?", (database_id,))
cursor.execute("DELETE FROM permissive WHERE session_id = ?", (database_id,))
cursor.execute("DELETE FROM value WHERE session_id = ?", (database_id,))
cursor.execute("DELETE FROM information WHERE session_id = ?", (database_id,))
cursor.execute("DELETE FROM session WHERE session_id = ?", (database_id,))
CONN.commit()
@asyncinit
class Storage:
__slots__ = ('_conn',
'_cursor',
'persistent',
'session_id',
'session_name',
'created')
storage = 'sqlite3'
async def __init__(self,
connection: Connection,
session_id: str,
persistent: bool):
delete_old_session: bool) -> None:
if not isinstance(session_id, str):
raise ValueError(_('session_id has to be a string'))
self.created = False
self.persistent = persistent
global CONN
init = False
if CONN is None:
init = True
CONN = sqlite3.connect(_gen_filename())
CONN.text_factory = str
self._conn = CONN
self._cursor = self._conn.cursor()
self.session_name = session_id
if init:
session_table = 'CREATE TABLE IF NOT EXISTS session(session_id INTEGER, '
session_table += 'session TEXT UNIQUE, persistent BOOL, PRIMARY KEY(session_id))'
settings_table = 'CREATE TABLE IF NOT EXISTS property(path TEXT, '
settings_table += 'tiram_index INTEGER, properties TEXT, session_id INTEGER, PRIMARY KEY(path, tiram_index, session_id), '
settings_table += 'FOREIGN KEY(session_id) REFERENCES session(session_id))'
permissives_table = 'CREATE TABLE IF NOT EXISTS permissive(path TEXT,'
permissives_table += 'tiram_index INTEGER, permissives TEXT, session_id INTEGER, PRIMARY KEY(path, tiram_index, session_id), '
permissives_table += 'FOREIGN KEY(session_id) REFERENCES session(session_id))'
values_table = 'CREATE TABLE IF NOT EXISTS value(path TEXT, '
values_table += 'value TEXT, owner TEXT, idx INTEGER, session_id INTEGER, '\
'PRIMARY KEY (path, idx, session_id), '
values_table += 'FOREIGN KEY(session_id) REFERENCES session(session_id))'
informations_table = 'CREATE TABLE IF NOT EXISTS information(key TEXT,'
informations_table += 'value TEXT, session_id INTEGER, path TEXT, '
informations_table += 'PRIMARY KEY (key, session_id), '
informations_table += 'FOREIGN KEY(session_id) REFERENCES session(session_id))'
self._cursor.execute(session_table)
self._cursor.execute(values_table)
self._cursor.execute(informations_table)
self._cursor.execute(settings_table)
self._cursor.execute(permissives_table)
commit_needed = True
else:
commit_needed = False
self.session_id = None
if self.persistent:
select = await self.select("SELECT session_id FROM session WHERE session = ?", (session_id,))
if select is not None:
self.session_name = session_id
select = await connection.select("SELECT session_id FROM session WHERE session = ?", (session_id,))
if select is not None:
if delete_old_session:
self.delete_session()
else:
self.session_id = select[0]
if self.session_id is None:
try:
self._cursor.execute('INSERT INTO session(session, persistent) VALUES (?, ?)',
(session_id, persistent))
except sqlite3.IntegrityError: # pragma: no cover
raise ConflictError(_('session "{}" already exists').format(session_id))
commit_needed = True
self.session_id = self._cursor.lastrowid
if commit_needed:
self._conn.commit()
await connection.execute('INSERT INTO session(session) VALUES (?)',
(session_id,))
self.session_id = connection.connection.lastrowid
self.created = True
async def commit(self) -> None:
self._conn.commit()
async def delete_session(self):
if self.session_id is not None:
await _delete_session(self.session_id,
CONN)
self.session_id = None
async def execute(self,
sql: str,
params: Optional[Dict]=None,
commit: bool=True) -> None:
#print(sql, params, commit)
if params is None:
params = tuple()
self._cursor.execute(sql, params)
if commit:
await self.commit()
async def list_sessions(self):
return await _list_sessions(self._cursor)
async def select(self,
sql: str,
params: Optional[Dict]=None,
only_one: bool=True) -> 'Row':
await self.execute(sql, params=params, commit=False)
if only_one:
return self._cursor.fetchone()
else:
return self._cursor.fetchall()
def __del__(self) -> None:
self._cursor.close()
if self.created and not self.persistent:
if delete_session is not None:
session_id = getattr(self, 'session_id', None)
delete_session(self.session_name,
session_id)
def getconnection(self):
return Connection()
def getsession():

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"default plugin for value: set it in a simple dictionary"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -32,50 +32,52 @@ class Values(Sqlite3DB):
super(Values, self).__init__(storage)
# sqlite
async def _sqlite_select(self, path, index):
async def _sqlite_select(self,
connection,
path,
index):
request = "SELECT value FROM value WHERE path = ? AND session_id = ? "
params = (path, self._session_id)
if index is not None:
request += "and idx = ? "
params = (path, self._session_id, index)
request += "LIMIT 1"
return await self._storage.select(request, params)
async def commit(self):
await self._storage.commit()
return await connection.select(request, params)
# value
async def setvalue(self,
connection,
path,
value,
owner,
index,
commit):
new=False):
"""set value for an option
a specified value must be associated to an owner
"""
log.debug('setvalue %s %s %s %s %s', path, value, owner, index, commit)
log.debug('setvalue %s %s %s %s', path, value, owner, index)
path = self._sqlite_encode_path(path)
if index is not None:
await self.resetvalue_index(path,
index,
commit=False)
await self._storage.execute("INSERT INTO value(path, value, owner, idx, session_id) VALUES "
"(?, ?, ?, ?, ?)", (path, self._sqlite_encode(value),
str(owner),
index,
self._session_id),
commit=commit)
else:
await self.resetvalue(path,
commit=False)
await self._storage.execute("INSERT INTO value(path, value, owner, session_id) VALUES "
"(?, ?, ?, ?)", (path, self._sqlite_encode(value),
if not new:
await self.resetvalue_index(connection,
path,
index)
await connection.execute("INSERT INTO value(path, value, owner, idx, session_id) VALUES "
"(?, ?, ?, ?, ?)", (path, self._sqlite_encode(value),
str(owner),
self._session_id),
commit=commit)
index,
self._session_id))
else:
if not new:
await self.resetvalue(connection,
path)
await connection.execute("INSERT INTO value(path, value, owner, session_id) VALUES "
"(?, ?, ?, ?)", (path, self._sqlite_encode(value),
str(owner),
self._session_id))
async def hasvalue(self,
connection,
path,
index=None):
"""if opt has a value
@ -83,44 +85,48 @@ class Values(Sqlite3DB):
"""
log.debug('hasvalue %s %s', path, index)
path = self._sqlite_encode_path(path)
return await self._sqlite_select(path, index) is not None
return await self._sqlite_select(connection,
path,
index) is not None
async def reduce_index(self, path, index):
async def reduce_index(self,
connection,
path,
index):
"""
_values == ((path1, path2), ((idx1_1, idx1_2), None),
((value1_1, value1_2), value2), ((owner1_1, owner1_2), owner2))
"""
log.debug('reduce_index %s %s %s', path, index, id(self))
await self._storage.execute("UPDATE value SET idx = ? WHERE path = ? and idx = ? "
"AND session_id = ?",
(index - 1, path, index, self._session_id))
await connection.execute("UPDATE value SET idx = ? WHERE path = ? and idx = ? "
"AND session_id = ?",
(index - 1, path, index, self._session_id))
async def resetvalue_index(self,
connection,
path,
index,
commit=True):
index):
"""remove value means delete value in storage
"""
log.debug('resetvalue_index %s %s %s', path, index, commit)
log.debug('resetvalue_index %s %s', path, index)
path = self._sqlite_encode_path(path)
await self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ? AND idx = ?",
(path, self._session_id, index),
commit=commit)
await connection.execute("DELETE FROM value WHERE path = ? AND session_id = ? AND idx = ?",
(path, self._session_id, index))
async def resetvalue(self,
path,
commit):
connection,
path):
"""remove value means delete value in storage
"""
log.debug('resetvalue %s %s', path, commit)
log.debug('resetvalue %s', path)
path = self._sqlite_encode_path(path)
await self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id),
commit=commit)
await connection.execute("DELETE FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id))
# owner
async def setowner(self,
connection,
path,
owner,
index=None):
@ -129,17 +135,18 @@ class Values(Sqlite3DB):
log.debug('setowner %s %s %s', path, owner, index)
path = self._sqlite_encode_path(path)
if index is None:
await self._storage.execute("UPDATE value SET owner = ? WHERE path = ? AND session_id = ?",
(str(owner), path, self._session_id))
await connection.execute("UPDATE value SET owner = ? WHERE path = ? AND session_id = ?",
(str(owner), path, self._session_id))
else:
await self._storage.execute("UPDATE value SET owner = ? WHERE path = ? and idx = ? AND session_id = ?",
(str(owner), path, index, self._session_id))
await connection.execute("UPDATE value SET owner = ? WHERE path = ? and idx = ? AND session_id = ?",
(str(owner), path, index, self._session_id))
async def getowner(self,
path,
default,
index=None,
with_value=False):
connection,
path,
default,
index=None,
with_value=False):
"""get owner for an option
return: owner object
"""
@ -152,7 +159,7 @@ class Values(Sqlite3DB):
else:
params = (path, self._session_id)
request += ' LIMIT 1'
owner = await self._storage.select(request, params)
owner = await connection.select(request, params)
if owner is None:
if not with_value:
return default
@ -171,7 +178,11 @@ class Values(Sqlite3DB):
value = self._sqlite_decode(owner[1])
return nowner, value
async def set_information(self, path, key, value):
async def set_information(self,
connection,
path,
key,
value):
"""updates the information's attribute
(which is a dictionary)
@ -180,22 +191,25 @@ class Values(Sqlite3DB):
"""
log.debug('set_information %s %s', key, value)
path = self._sqlite_encode_path(path)
await self._storage.execute("DELETE FROM information WHERE key = ? AND session_id = ? AND path = ?",
(key, self._session_id, path),
False)
await self._storage.execute("INSERT INTO information(key, value, session_id, path) VALUES "
"(?, ?, ?, ?)", (key, self._sqlite_encode(value), self._session_id, path))
await connection.execute("DELETE FROM information WHERE key = ? AND session_id = ? AND path = ?",
(key, self._session_id, path))
await connection.execute("INSERT INTO information(key, value, session_id, path) VALUES "
"(?, ?, ?, ?)", (key, self._sqlite_encode(value), self._session_id, path))
async def get_information(self, path, key, default):
async def get_information(self,
connection,
path,
key,
default):
"""retrieves one information's item
:param key: the item string (ex: "help")
"""
log.debug('get_information %s %s', key, default)
path = self._sqlite_encode_path(path)
value = await self._storage.select("SELECT value FROM information WHERE key = ? AND "
"session_id = ? AND path = ?",
(key, self._session_id, path))
value = await connection.select("SELECT value FROM information WHERE key = ? AND "
"session_id = ? AND path = ?",
(key, self._session_id, path))
if value is None:
if default is undefined:
raise ValueError(_("information's item"
@ -204,35 +218,43 @@ class Values(Sqlite3DB):
else:
return self._sqlite_decode(value[0])
async def del_information(self, path, key, raises):
async def del_information(self,
connection,
path,
key,
raises):
log.debug('del_information %s %s', key, raises)
path = self._sqlite_encode_path(path)
information = await self._storage.select("SELECT value FROM information WHERE key = ? "
"AND session_id = ? AND path = ?",
(key, self._session_id, path))
information = await connection.select("SELECT value FROM information WHERE key = ? "
"AND session_id = ? AND path = ?",
(key, self._session_id, path))
if raises and information is None:
raise ValueError(_("information's item not found {0}").format(key))
await self._storage.execute("DELETE FROM information WHERE key = ? AND session_id = ? AND path = ?",
(key, self._session_id, path))
await connection.execute("DELETE FROM information WHERE key = ? AND session_id = ? AND path = ?",
(key, self._session_id, path))
async def list_information(self, path):
async def list_information(self,
connection,
path):
path = self._sqlite_encode_path(path)
rows = await self._storage.select("SELECT key FROM information WHERE session_id = ? AND path = ?",
(self._session_id, path),
only_one=False)
rows = await connection.select("SELECT key FROM information WHERE session_id = ? AND path = ?",
(self._session_id, path),
only_one=False)
ret = []
for row in rows:
ret.append(self._sqlite_decode_path(row[0]))
return ret
async def del_informations(self):
await self._storage.execute("DELETE FROM information WHERE session_id = ?",
(self._session_id,))
async def del_informations(self,
connection):
await connection.execute("DELETE FROM information WHERE session_id = ?",
(self._session_id,))
async def exportation(self):
async def exportation(self,
connection):
log.debug('exportation')
rows = await self._storage.select("SELECT path, value, owner, idx FROM value WHERE "
"session_id = ?;", (self._session_id,), only_one=False)
rows = await connection.select("SELECT path, value, owner, idx FROM value WHERE "
"session_id = ?;", (self._session_id,), only_one=False)
ret = [[], [], [], []]
for row in rows:
path = self._sqlite_decode_path(row[0])
@ -258,36 +280,36 @@ class Values(Sqlite3DB):
return ret
async def importation(self, export):
async def importation(self,
connection,
export):
log.debug('importation')
request = "DELETE FROM value WHERE session_id = ?"
await self._storage.execute(request, (self._session_id,),
commit=False)
await connection.execute(request, (self._session_id,))
for idx, path in enumerate(export[0]):
path = self._sqlite_encode_path(path)
index = export[1][idx]
value = export[2][idx]
owner = export[3][idx]
if index is None:
await self._storage.execute("INSERT INTO value(path, value, owner, idx, session_id) VALUES "
"(?, ?, ?, ?, ?)", (path, self._sqlite_encode(value),
str(owner), index,
self._session_id), commit=False)
await connection.execute("INSERT INTO value(path, value, owner, idx, session_id) VALUES "
"(?, ?, ?, ?, ?)", (path, self._sqlite_encode(value),
str(owner), index,
self._session_id))
else:
for val in zip(index, value, owner):
await self._storage.execute("INSERT INTO value(path, value, owner, idx, session_id)"
"VALUES (?, ?, ?, ?, ?)", (path,
self._sqlite_encode(val[1]),
str(val[2]), val[0],
self._session_id),
commit=False)
self._storage._conn.commit()
await connection.execute("INSERT INTO value(path, value, owner, idx, session_id)"
"VALUES (?, ?, ?, ?, ?)", (path,
self._sqlite_encode(val[1]),
str(val[2]), val[0],
self._session_id))
async def get_max_length(self,
connection,
path):
log.debug('get_max_length %s', path)
val_max = await self._storage.select("SELECT max(idx) FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id), False)
val_max = await connection.select("SELECT max(idx) FROM value WHERE path = ? AND session_id = ?",
(path, self._session_id), False)
if val_max[0][0] is None:
return 0
return val_max[0][0] + 1

View File

@ -606,7 +606,7 @@ class TiramisuDict:
old_properties = childapi._option_bag.config_bag.properties
config = childapi._option_bag.config_bag.context
settings = config.cfgimpl_get_settings()
childapi._option_bag.config_bag.properties = await settings.get_context_properties(config._impl_properties_cache)
childapi._option_bag.config_bag.properties = await self.config.property.get(default=True) # settings.get_context_properties(config._impl_properties_cache)
childapi._option_bag.config_bag.properties -= {'permissive'}
properties = await childapi.property.get(only_raises=True,
uncalculated=True)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"takes care of the option's values and multi values"
# Copyright (C) 2013-2019 Team tiramisu (see AUTHORS for all contributors)
# Copyright (C) 2013-2020 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
@ -34,7 +34,8 @@ class Values:
'__weakref__')
async def __init__(self,
storage):
storage,
connection):
"""
Initializes the values's dict.
@ -44,13 +45,17 @@ class Values:
# store the storage
self._p_ = storage
# set default owner
owner = await self._p_.getowner(None, None)
owner = await self._p_.getowner(connection,
None,
None,
None)
if owner is None:
await self._p_.setvalue(None,
await self._p_.setvalue(connection,
None,
None,
owners.user,
None,
True)
new=True)
#______________________________________________________________________
# get value
@ -105,7 +110,10 @@ class Values:
if 'force_metaconfig_on_freeze' in option_bag.properties:
settings = option_bag.config_bag.context.cfgimpl_get_settings()
if 'force_metaconfig_on_freeze' in option_bag.option.impl_getproperties() and \
not await settings._p_.getproperties(option_bag.path, None, frozenset()):
not await settings._p_.getproperties(option_bag.config_bag.connection,
option_bag.path,
None,
frozenset()):
# if force_metaconfig_on_freeze is only in option (not in config)
return option_bag.config_bag.context.impl_type == 'config'
else:
@ -143,7 +151,8 @@ class Values:
_index = None
else:
_index = index
owner, value = await self._p_.getowner(option_bag.path,
owner, value = await self._p_.getowner(option_bag.config_bag.connection,
option_bag.path,
owners.default,
index=_index,
with_value=True)
@ -286,10 +295,9 @@ class Values:
# set value
async def setvalue(self,
value,
option_bag,
_commit):
option_bag):
context = option_bag.config_bag.context
owner = await self.get_context_owner()
owner = await self.get_context_owner(option_bag.config_bag.connection)
if 'validator' in option_bag.config_bag.properties:
await self.setvalue_validation(value,
option_bag)
@ -299,8 +307,7 @@ class Values:
value = value.copy()
await self._setvalue(option_bag,
value,
owner,
commit=False)
owner)
setting_properties = option_bag.config_bag.properties
validator = 'validator' in setting_properties and 'demoting_error_warning' not in setting_properties
if validator:
@ -315,10 +322,7 @@ class Values:
await option_bag.option.impl_get_leadership().follower_force_store_value(self,
value,
option_bag,
owners.forced,
_commit=_commit)
if _commit:
await self._p_.commit()
owners.forced)
async def setvalue_validation(self,
value,
@ -343,14 +347,13 @@ class Values:
async def _setvalue(self,
option_bag,
value,
owner,
commit=True):
owner):
await option_bag.config_bag.context.cfgimpl_reset_cache(option_bag)
await self._p_.setvalue(option_bag.path,
await self._p_.setvalue(option_bag.config_bag.connection,
option_bag.path,
value,
owner,
option_bag.index,
commit)
option_bag.index)
async def _get_modified_parent(self,
option_bag: OptionBag) -> Optional[OptionBag]:
@ -420,13 +423,15 @@ class Values:
'force_default_on_freeze' in option_bag.properties:
return owners.default
if only_default:
if await self._p_.hasvalue(option_bag.path,
if await self._p_.hasvalue(option_bag.config_bag.connection,
option_bag.path,
option_bag.index):
owner = 'not_default'
else:
owner = owners.default
else:
owner = await self._p_.getowner(option_bag.path,
owner = await self._p_.getowner(option_bag.config_bag.connection,
option_bag.path,
owners.default,
index=option_bag.index)
if validate_meta is not False and (owner is owners.default or \
@ -455,26 +460,27 @@ class Values:
if owner in forbidden_owners:
raise ValueError(_('set owner "{0}" is forbidden').format(str(owner)))
if not await self._p_.hasvalue(option_bag.path):
if not await self._p_.hasvalue(option_bag.config_bag.connection,
option_bag.path):
raise ConfigError(_('no value for {0} cannot change owner to {1}'
'').format(option_bag.path, owner))
option_bag.config_bag.context.cfgimpl_get_settings().validate_frozen(option_bag)
await self._p_.setowner(option_bag.path,
owner,
index=option_bag.index)
await self._p_.setowner(option_bag.config_bag.connection,
option_bag.path,
owner,
index=option_bag.index)
#______________________________________________________________________
# reset
async def reset(self,
option_bag,
_commit=True):
option_bag):
context = option_bag.config_bag.context
hasvalue = await self._p_.hasvalue(option_bag.path)
hasvalue = await self._p_.hasvalue(option_bag.config_bag.connection,
option_bag.path)
setting_properties = option_bag.config_bag.properties
if hasvalue and 'validator' in option_bag.config_bag.properties:
fake_context = await context._gen_fake_values()
fake_context = await context._gen_fake_values(option_bag.config_bag.connection)
config_bag = option_bag.config_bag.copy()
config_bag.remove_validation()
config_bag.context = fake_context
@ -489,21 +495,19 @@ class Values:
opt = option_bag.option
if opt.impl_is_leader():
await opt.impl_get_leadership().reset(self,
option_bag,
_commit=_commit)
option_bag)
if hasvalue:
if 'force_store_value' in option_bag.config_bag.properties and 'force_store_value' in option_bag.properties:
value = await self.getdefaultvalue(option_bag)
await self._setvalue(option_bag,
value,
owners.forced,
commit=_commit)
owners.forced)
else:
# for leader only
value = None
await self._p_.resetvalue(option_bag.path,
_commit)
await self._p_.resetvalue(option_bag.config_bag.connection,
option_bag.path)
await context.cfgimpl_reset_cache(option_bag)
if 'force_store_value' in setting_properties and option_bag.option.impl_is_leader():
if value is None:
@ -511,17 +515,17 @@ class Values:
await option_bag.option.impl_get_leadership().follower_force_store_value(self,
value,
option_bag,
owners.forced,
_commit=_commit)
owners.forced)
async def reset_follower(self,
option_bag,
_commit=True):
if await self._p_.hasvalue(option_bag.path, index=option_bag.index):
option_bag):
if await self._p_.hasvalue(option_bag.config_bag.connection,
option_bag.path,
index=option_bag.index):
context = option_bag.config_bag.context
setting_properties = option_bag.config_bag.properties
if 'validator' in setting_properties:
fake_context = await context._gen_fake_values()
fake_context = await context._gen_fake_values(option_bag.config_bag.connection)
fake_value = fake_context.cfgimpl_get_values()
config_bag = option_bag.config_bag.copy()
config_bag.remove_validation()
@ -537,12 +541,11 @@ class Values:
await self._setvalue(option_bag,
value,
owners.forced,
commit=_commit)
owners.forced)
else:
await self._p_.resetvalue_index(option_bag.path,
option_bag.index,
_commit)
await self._p_.resetvalue_index(option_bag.config_bag.connection,
option_bag.path,
option_bag.index)
await context.cfgimpl_reset_cache(option_bag)
async def reset_leadership(self,
@ -561,13 +564,13 @@ class Values:
index,
option_bag)
await self.setvalue(current_value,
option_bag,
_commit=True)
option_bag)
#______________________________________________________________________
# information
async def set_information(self,
connection,
key,
value,
path=None):
@ -576,11 +579,13 @@ class Values:
:param key: information's key (ex: "help", "doc"
:param value: information's value (ex: "the help string")
"""
await self._p_.set_information(path,
await self._p_.set_information(connection,
path,
key,
value)
async def get_information(self,
connection,
key,
default=undefined,
path=None):
@ -588,21 +593,26 @@ class Values:
:param key: the item string (ex: "help")
"""
return await self._p_.get_information(path,
return await self._p_.get_information(connection,
path,
key,
default)
async def del_information(self,
connection,
key,
raises=True,
path=None):
await self._p_.del_information(path,
await self._p_.del_information(connection,
path,
key,
raises)
async def list_information(self,
connection,
path=None):
return await self._p_.list_information(path)
return await self._p_.list_information(connection,
path)
#______________________________________________________________________
# mandatory warnings
@ -675,18 +685,20 @@ class Values:
od_setting_properties = config_bag.properties - {'mandatory', 'empty'}
setting_properties = set(config_bag.properties) - {'warnings'}
setting_properties.update(['mandatory', 'empty'])
config_bag = ConfigBag(context=config_bag.context,
properties=frozenset(setting_properties),
permissives=config_bag.permissives)
config_bag.set_permissive()
od_config_bag = ConfigBag(context=config_bag.context,
nconfig_bag = ConfigBag(context=config_bag.context,
properties=frozenset(setting_properties),
permissives=config_bag.permissives)
nconfig_bag.connection = config_bag.connection
nconfig_bag.set_permissive()
od_config_bag = ConfigBag(context=nconfig_bag.context,
properties=frozenset(od_setting_properties),
permissives=config_bag.permissives)
permissives=nconfig_bag.permissives)
od_config_bag.connection = config_bag.connection
od_config_bag.set_permissive()
descr = context.cfgimpl_get_description()
async for option in self._mandatory_warnings(context,
config_bag,
nconfig_bag,
descr,
[],
context,
@ -696,15 +708,20 @@ class Values:
#____________________________________________________________
# default owner methods
async def set_context_owner(self,
connection,
owner):
":param owner: sets the default value for owner at the Config level"
if owner in forbidden_owners:
raise ValueError(_('set owner "{0}" is forbidden').format(str(owner)))
await self._p_.setowner(None,
await self._p_.setowner(connection,
None,
owner,
index=None)
async def get_context_owner(self):
return await self._p_.getowner(None,
async def get_context_owner(self,
connection):
return await self._p_.getowner(connection,
None,
None,
None)