New Postgres storage
This commit is contained in:
@ -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
|
||||
|
768
tiramisu/api.py
768
tiramisu/api.py
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
|
2
tiramisu/storage/cache/dictionary.py
vendored
2
tiramisu/storage/cache/dictionary.py
vendored
@ -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
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
32
tiramisu/storage/postgres/__init__.py
Normal file
32
tiramisu/storage/postgres/__init__.py
Normal 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')
|
179
tiramisu/storage/postgres/setting.py
Normal file
179
tiramisu/storage/postgres/setting.py
Normal 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()
|
183
tiramisu/storage/postgres/storage.py
Normal file
183
tiramisu/storage/postgres/storage.py
Normal 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()
|
298
tiramisu/storage/postgres/value.py
Normal file
298
tiramisu/storage/postgres/value.py
Normal 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()
|
@ -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')
|
||||
|
@ -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,
|
||||
))
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user