diff --git a/test/test_choice_option.py b/test/test_choice_option.py index e1978dc..86fa792 100644 --- a/test/test_choice_option.py +++ b/test/test_choice_option.py @@ -189,6 +189,44 @@ def test_choiceoption_calc_opt_multi_function(): raises(ValueError, "cfg.option('ch2').value.get()") +def test_choiceoption_calc_opt_multi_function_kwargs(): + str_ = StrOption('str', '', ['val1'], multi=True) + choice = ChoiceOption('choice', + "", + default_multi='val2', + values=return_val, + values_params=Params(kwargs={'val': ParamOption(str_)}), + multi=True) + ch2 = ChoiceOption('ch2', + "", + default=['val2'], + values=return_val, + values_params=Params(kwargs={'val': ParamOption(str_)}), + multi=True) + odesc = OptionDescription('od', '', [str_, choice, ch2]) + cfg = Config(odesc) + cfg.property.read_write() + owner = cfg.owner.get() + assert cfg.option('choice').owner.isdefault() + assert cfg.option('choice').value.get() == [] + # + cfg.option('choice').value.set(['val1']) + assert cfg.option('choice').owner.get() == owner + # + raises(ValueError, "cfg.option('choice').value.set([undefined])") + # + cfg.option('choice').value.set(['val1']) + assert cfg.option('choice').owner.get() == owner + # + cfg.option('choice').value.reset() + assert cfg.option('choice').owner.isdefault() + # + raises(ValueError, "cfg.option('choice').value.set('no')") + assert cfg.option('choice').owner.isdefault() + # + raises(ValueError, "cfg.option('ch2').value.get()") + + def test_choiceoption_calc_invalid(): str_ = StrOption('str', '', ['val1'], multi=True) str_ diff --git a/test/test_option_callback.py b/test/test_option_callback.py index d1883f6..4ff3b50 100644 --- a/test/test_option_callback.py +++ b/test/test_option_callback.py @@ -361,6 +361,17 @@ def test_callback_value_force_permissive(): api.option('val3').value.get() is None +def test_callback_value_force_permissive_kwargs(): + val1 = StrOption('val1', "", 'val', properties=('disabled',)) + val2 = StrOption('val2', "", callback=return_value, callback_params=Params(value=ParamOption(val1))) + val3 = StrOption('val3', "", callback=return_value, callback_params=Params(value=ParamOption(val1, True))) + maconfig = OptionDescription('rootconfig', '', [val1, val2, val3]) + api = Config(maconfig) + api.property.read_only() + raises(ConfigError, "api.option('val2').value.get()") + api.option('val3').value.get() is None + + def test_callback_symlink(): val1 = StrOption('val1', "", 'val') val2 = SymLinkOption('val2', val1) diff --git a/test/test_permissive.py b/test/test_permissive.py index 84a5d77..dcc3921 100644 --- a/test/test_permissive.py +++ b/test/test_permissive.py @@ -5,7 +5,7 @@ do_autopath() from py.test import raises from tiramisu import IntOption, UnicodeOption, OptionDescription, Config -from tiramisu.error import PropertiesOptionError +from tiramisu.error import PropertiesOptionError, ConfigError from tiramisu.api import TIRAMISU_VERSION @@ -102,12 +102,18 @@ def test_permissive_frozen(): assert frozenset(props) == frozenset(['disabled']) -if TIRAMISU_VERSION == 3: - def test_invalid_permissive(): - descr = make_description() - api = Config(descr) - api.property.read_write() - raises(TypeError, "api.unrestraint.permissive.set(['frozen', 'disabled'])") +def test_invalid_permissive(): + descr = make_description() + api = Config(descr) + api.property.read_write() + raises(TypeError, "api.unrestraint.permissive.set(['frozen', 'disabled'])") + + +def test_forbidden_permissive(): + descr = make_description() + api = Config(descr) + api.property.read_write() + raises(ConfigError, "api.permissive.set(frozenset(['force_default_on_freeze']))") def test_permissive_option(): diff --git a/test/test_storage.py b/test/test_storage.py index 7e70578..86b4fd2 100644 --- a/test/test_storage.py +++ b/test/test_storage.py @@ -323,23 +323,41 @@ def test_exportation_importation(): try: c = Config(o, session_id='test_persistent', persistent=True) d = Config(o, session_id='test_persistent2', persistent=True) + e = Config(o, session_id='test_persistent3', persistent=True) except ValueError: # storage is not persistent pass else: + c.owner.set('export') assert c.option('b').value.get() is None c.option('b').value.set(True) assert c.option('b').value.get() is True + assert c.owner.get() == 'export' del c # c = Config(o, session_id='test_persistent', persistent=True) - assert c.value.exportation() == [['b'], [None], [True], ['user']] + assert c.owner.get() == 'export' + assert c.value.exportation() == [['b'], [None], [True], ['export']] d.value.importation(c.value.exportation()) - assert c.value.exportation() == [['b'], [None], [True], ['user']] - assert d.value.exportation() == [['b'], [None], [True], ['user']] + assert c.value.exportation() == [['b'], [None], [True], ['export']] + assert c.owner.get() == 'export' + assert d.value.exportation() == [['b'], [None], [True], ['export']] + assert d.owner.get() == 'user' del d # d = Config(o, session_id='test_persistent2', persistent=True) - assert d.value.exportation() == [['b'], [None], [True], ['user']] + assert d.value.exportation() == [['b'], [None], [True], ['export']] + assert d.owner.get() == 'user' + # + e.value.importation(c.value.exportation(with_default_owner=True)) + assert e.value.exportation() == [['b'], [None], [True], ['export']] + assert e.owner.get() == 'export' + del e + # + e = Config(o, session_id='test_persistent3', persistent=True) + assert e.value.exportation() == [['b'], [None], [True], ['export']] + assert e.owner.get() == 'export' + # delete_session('test_persistent') delete_session('test_persistent2') + delete_session('test_persistent3') diff --git a/test/test_symlink.py b/test/test_symlink.py index ab08b05..255b910 100644 --- a/test/test_symlink.py +++ b/test/test_symlink.py @@ -63,6 +63,16 @@ def test_symlink_addproperties(): raises(TypeError, "api.option('c').property.reset()") +def test_symlink_getpermissive(): + boolopt = BoolOption('b', '', default=True, properties=('test',)) + linkopt = SymLinkOption("c", boolopt) + descr = OptionDescription('opt', '', [boolopt, linkopt]) + api = Config(descr) + api.property.read_write() + api.option('b').permissive.set(frozenset(['perm'])) + api.option('c').permissive.get() == frozenset(['perm']) + + def test_symlink_addpermissive(): boolopt = BoolOption('b', '', default=True, properties=('test',)) linkopt = SymLinkOption("c", boolopt) diff --git a/tiramisu/api.py b/tiramisu/api.py index e088811..fc61fd8 100644 --- a/tiramisu/api.py +++ b/tiramisu/api.py @@ -778,8 +778,18 @@ class TiramisuContextValue(TiramisuContext): def importation(self, values): """import values""" + if None not in values[0]: + context_owner = self.config_bag.context.cfgimpl_get_values().get_context_owner() + else: + context_owner = None self.config_bag.context.cfgimpl_get_values()._p_.importation(values) self.config_bag.context.cfgimpl_reset_cache(None, None) + if context_owner is not None: + self.config_bag.context.cfgimpl_get_values()._p_.setvalue(None, + None, + context_owner, + None, + True) class TiramisuContextOwner(TiramisuContext): diff --git a/tiramisu/function.py b/tiramisu/function.py index 394b12c..8b6e498 100644 --- a/tiramisu/function.py +++ b/tiramisu/function.py @@ -17,11 +17,13 @@ from .i18n import _ class Params: __slots__ = ('args', 'kwargs') - def __init__(self, args=None, kwargs=None): + def __init__(self, args=None, kwargs=None, **kwgs): if args is None: args = tuple() if kwargs is None: kwargs = {} + if kwgs: + kwargs.update(kwgs) if isinstance(args, Param): args = (args,) else: diff --git a/tiramisu/option/baseoption.py b/tiramisu/option/baseoption.py index b0d9599..7d989ee 100644 --- a/tiramisu/option/baseoption.py +++ b/tiramisu/option/baseoption.py @@ -270,7 +270,7 @@ class Base(object): 'callback') # first part is validator val = getattr(self, '_val_call', (None,))[0] - if callback_params == {}: + if not callback_params: val_call = (callback,) else: val_call = (callback, callback_params) @@ -321,9 +321,9 @@ class Base(object): def impl_get_callback(self): call = getattr(self, '_val_call', (None, None))[1] if call is None: - ret_call = (None, {}) + ret_call = (None, None) elif len(call) == 1: - ret_call = (call[0], {}) + ret_call = (call[0], None) else: ret_call = call return ret_call diff --git a/tiramisu/option/choiceoption.py b/tiramisu/option/choiceoption.py index a41cb93..e6b47d8 100644 --- a/tiramisu/option/choiceoption.py +++ b/tiramisu/option/choiceoption.py @@ -58,7 +58,7 @@ class ChoiceOption(Option): values_params = self._build_calculator_params(values, values_params, 'values') - if values_params != {}: + if values_params: self._choice_values_params = values_params else: if values_params is not None: diff --git a/tiramisu/option/dynsymlinkoption.py b/tiramisu/option/dynsymlinkoption.py index e6b0066..2e814b0 100644 --- a/tiramisu/option/dynsymlinkoption.py +++ b/tiramisu/option/dynsymlinkoption.py @@ -51,9 +51,6 @@ class DynSymLinkOption(object): def impl_get_display_name(self): return self._opt.impl_get_display_name(dyn_name=self.impl_getname()) - def impl_getopt(self): - return self._opt - def impl_getsuffix(self): return self._suffix diff --git a/tiramisu/option/option.py b/tiramisu/option/option.py index 1592ca8..361a71b 100644 --- a/tiramisu/option/option.py +++ b/tiramisu/option/option.py @@ -106,7 +106,7 @@ class Option(OnlyOption): validator_params, 'validator', add_value=True) - if validator_params == {}: + if not validator_params: val_call = (validator,) else: val_call = (validator, validator_params) @@ -165,7 +165,7 @@ class Option(OnlyOption): def _validate(self, *args, - **kwargs): + **kwargs): # pragma: no cover pass def impl_is_unique(self): @@ -174,9 +174,9 @@ class Option(OnlyOption): def impl_get_validator(self): val = getattr(self, '_val_call', (None,))[0] if val is None: - ret_val = (None, {}) + ret_val = (None, None) elif len(val) == 1: - ret_val = (val[0], {}) + ret_val = (val[0], None) else: ret_val = val return ret_val @@ -188,12 +188,8 @@ class Option(OnlyOption): check_error=True): """ """ - if option_bag: - config_bag = option_bag.config_bag - force_index = option_bag.index - else: - config_bag = undefined - force_index = None + config_bag = option_bag.config_bag + force_index = option_bag.index is_warnings_only = getattr(self, '_warnings_only', False) if check_error and config_bag is not undefined and \ @@ -364,9 +360,9 @@ class Option(OnlyOption): for opt in other_opts: if isinstance(opt, weakref.ReferenceType): opt = opt() - if opt.impl_is_submulti(): + if opt.impl_is_submulti(): # pragma: no cover raise ConfigError(_('cannot add consistency with submulti option')) - if not isinstance(opt, Option): + if not isinstance(opt, Option): # pragma: no cover raise ConfigError(_('consistency must be set with an option, not {}').format(opt)) if opt.issubdyn(): if dynod is None: @@ -585,7 +581,6 @@ class Option(OnlyOption): for opt in opts: if isinstance(opt, weakref.ReferenceType): opt = opt() - is_multi = False try: opt_value = self._get_consistency_value(option_bag.index, opt, @@ -612,39 +607,27 @@ class Option(OnlyOption): raise ValueError(_('unexpected length of "{}" in constency "{}", ' 'should be "{}"').format(len(opt_value), opt.impl_get_display_name(), - length)) - else: - length = len_value - is_multi = True + length)) # pragma: no cover + length = len_value if isinstance(opt_value, list) and func in ALLOWED_CONST_LIST: for value_ in opt_value: if opt.impl_is_submulti(): for val in value_: - all_cons_vals.append((False, val)) + all_cons_vals.append( val) all_cons_opts.append(opt) else: - all_cons_vals.append((False, value_)) + all_cons_vals.append(value_) all_cons_opts.append(opt) else: - all_cons_vals.append((is_multi, opt_value)) + all_cons_vals.append(opt_value) all_cons_opts.append(opt) if config_bag is not undefined and not 'validator' in config_bag.properties: return all_values = [] if length is None: - all_value = [] - for is_multi, values in all_cons_vals: - all_value.append(values) - all_values = [all_value] - else: - for idx in range(length): - all_value = [] - for is_multi, values in all_cons_vals: - if not is_multi: - all_value.append(values) - else: - all_value.append(values[idx]) - all_values.append(all_value) + all_values = [all_cons_vals] + elif length: + all_values = zip(*all_cons_vals) try: for values in all_values: getattr(self, func)(current_opt, @@ -773,16 +756,10 @@ class Option(OnlyOption): else: consistencies.append(cons) - def _del_consistency(self): - self._consistencies.pop(-1) - def get_consistencies(self): return getattr(self, '_consistencies', STATIC_TUPLE) def _has_consistencies(self, context): - if context is undefined: - return False - descr = context.cfgimpl_get_description() if descr._cache_consistencies is None: return False @@ -796,9 +773,7 @@ class RegexpOption(Option): value, *args, **kwargs): - err = self._impl_valid_string(value) - if err: - return err + self._impl_valid_string(value) match = self._regexp.search(value) if not match: raise ValueError() diff --git a/tiramisu/setting.py b/tiramisu/setting.py index 86623d6..c1fc8dc 100644 --- a/tiramisu/setting.py +++ b/tiramisu/setting.py @@ -106,7 +106,7 @@ rw_remove = set(['permissive', 'everything_frozen', 'mandatory', 'empty']) FORBIDDEN_SET_PROPERTIES = frozenset(['force_store_value']) -FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze']) +FORBIDDEN_SET_PERMISSIVES = frozenset(['force_default_on_freeze', 'force_default_on_freeze']) log = getLogger('tiramisu') @@ -154,12 +154,12 @@ class OptionBag: return self.option elif key == 'apply_requires': return True - raise KeyError('unknown key {} for OptionBag'.format(key)) + raise KeyError('unknown key {} for OptionBag'.format(key)) # pragma: no cover def __delattr__(self, key): if key == 'properties': return - raise KeyError('unknown key {} for ConfigBag'.format(key)) + raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover def copy(self): kwargs = {} @@ -186,7 +186,7 @@ class ConfigBag: if key == 'permissives': self.permissives = self.context.cfgimpl_get_settings().get_context_permissives() return self.permissives - raise KeyError('unknown key {} for ConfigBag'.format(key)) + raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover def remove_validation(self): self.properties = frozenset(self.properties - {'validator'}) @@ -201,7 +201,7 @@ class ConfigBag: except AttributeError: pass return - raise KeyError('unknown key {} for ConfigBag'.format(key)) + raise KeyError('unknown key {} for ConfigBag'.format(key)) # pragma: no cover #def __setattr__(self, key, value): # super().__setattr__(key, value) @@ -610,10 +610,7 @@ class Settings(object): #values too because of slave values could have a PropertiesOptionError has value context.cfgimpl_reset_cache(option_bag) if option_bag is not None: - try: - del option_bag.properties - except AttributeError: - pass + del option_bag.properties def set_context_permissives(self, permissives): @@ -661,7 +658,7 @@ class Settings(object): opt = None else: opt = option_bag.option - if all_properties and option_bag: + if all_properties and option_bag: # pragma: no cover raise ValueError(_('opt and all_properties must not be set ' 'together in reset')) if opt and opt.impl_is_symlinkoption(): diff --git a/tiramisu/storage/dictionary/value.py b/tiramisu/storage/dictionary/value.py index eb9ba6b..4b90d82 100644 --- a/tiramisu/storage/dictionary/value.py +++ b/tiramisu/storage/dictionary/value.py @@ -298,5 +298,6 @@ class Values(Cache): def importation(self, export): self._values = export + def delete_session(session_id): raise ValueError(_('cannot delete none persistent session')) diff --git a/tiramisu/storage/sqlite3/storage.py b/tiramisu/storage/sqlite3/storage.py index 040e04c..f872e77 100644 --- a/tiramisu/storage/sqlite3/storage.py +++ b/tiramisu/storage/sqlite3/storage.py @@ -67,10 +67,11 @@ global CONN CONN = None class Storage(object): - __slots__ = ('_conn', '_cursor', 'persistent', 'session_id', 'session_name') + __slots__ = ('_conn', '_cursor', 'persistent', 'session_id', 'session_name', 'created') storage = 'sqlite3' def __init__(self, session_id, persistent, test=False): + self.created = False self.persistent = persistent global CONN init = False @@ -115,12 +116,13 @@ class Storage(object): except sqlite3.IntegrityError: raise ConflictError(_('session "{}" already used').format(session_id)) self.session_id = self._cursor.lastrowid + self.created = True def commit(self): self._conn.commit() def execute(self, sql, params=None, commit=True): - #print(sql, params) + #print(sql, params, commit) if params is None: params = tuple() self._cursor.execute(sql, params) @@ -136,7 +138,7 @@ class Storage(object): def __del__(self): self._cursor.close() - if not self.persistent: + 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, diff --git a/tiramisu/storage/sqlite3/value.py b/tiramisu/storage/sqlite3/value.py index 915e429..b470e10 100644 --- a/tiramisu/storage/sqlite3/value.py +++ b/tiramisu/storage/sqlite3/value.py @@ -57,7 +57,7 @@ class Values(Sqlite3DB): """set value for an option a specified value must be associated to an owner """ - if DEBUG: + if DEBUG: # pragma: no cover print('setvalue', path, value, owner, index, commit) path = self._sqlite_encode_path(path) if index is not None: @@ -85,7 +85,7 @@ class Values(Sqlite3DB): """if opt has a value return: boolean """ - if DEBUG: + if DEBUG: # pragma: no cover print('hasvalue', path, index) path = self._sqlite_encode_path(path) return self._sqlite_select(path, index) is not None @@ -108,7 +108,7 @@ class Values(Sqlite3DB): commit=True): """remove value means delete value in storage """ - if DEBUG: + if DEBUG: # pragma: no cover print('resetvalue_index', path, index, commit) path = self._sqlite_encode_path(path) self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ? AND idx = ?", @@ -120,7 +120,7 @@ class Values(Sqlite3DB): commit): """remove value means delete value in storage """ - if DEBUG: + if DEBUG: # pragma: no cover print('resetvalue', path, commit) path = self._sqlite_encode_path(path) self._storage.execute("DELETE FROM value WHERE path = ? AND session_id = ?", @@ -134,7 +134,7 @@ class Values(Sqlite3DB): index=None): """change owner for an option """ - if DEBUG: + if DEBUG: # pragma: no cover print('setowner', path, owner, index) path = self._sqlite_encode_path(path) if index is None: @@ -152,7 +152,7 @@ class Values(Sqlite3DB): """get owner for an option return: owner object """ - if DEBUG: + if DEBUG: # pragma: no cover print('getowner', path, default, index, with_value) path = self._sqlite_encode_path(path) request = "SELECT owner, value FROM value WHERE path = ? AND session_id = ?" @@ -188,7 +188,7 @@ class Values(Sqlite3DB): :param key: information's key (ex: "help", "doc" :param value: information's value (ex: "the help string") """ - if DEBUG: + if DEBUG: # pragma: no cover print('set_information', key, value) path = self._sqlite_encode_path(path) self._storage.execute("DELETE FROM information WHERE key = ? AND session_id = ? AND path = ?", @@ -202,7 +202,7 @@ class Values(Sqlite3DB): :param key: the item string (ex: "help") """ - if DEBUG: + if DEBUG: # pragma: no cover print('get_information', key, default) path = self._sqlite_encode_path(path) value = self._storage.select("SELECT value FROM information WHERE key = ? AND " @@ -217,7 +217,7 @@ class Values(Sqlite3DB): return self._sqlite_decode(value[0]) def del_information(self, path, key, raises): - if DEBUG: + if DEBUG: # pragma: no cover print('del_information', key, raises) path = self._sqlite_encode_path(path) if raises and self._storage.select("SELECT value FROM information WHERE key = ? " @@ -232,13 +232,13 @@ class Values(Sqlite3DB): (self._session_id,)) def exportation(self): - if DEBUG: + if DEBUG: # pragma: no cover print('exportation') rows = self._storage.select("SELECT path, value, owner, idx FROM value WHERE " "session_id = ?;", (self._session_id,), only_one=False) ret = [[], [], [], []] for row in rows: - path = row[0] + path = self._sqlite_decode_path(row[0]) value = self._sqlite_decode(row[1]) owner = row[2] index = row[3] @@ -262,9 +262,10 @@ class Values(Sqlite3DB): return ret def importation(self, export): - if DEBUG: + if DEBUG: # pragma: no cover print('importation') - self._storage.execute("DELETE FROM value WHERE session_id = ?", (self._session_id,), + request = "DELETE FROM value WHERE session_id = ?" + self._storage.execute(request, (self._session_id,), commit=False) for idx, path in enumerate(export[0]): path = self._sqlite_encode_path(path) @@ -288,7 +289,7 @@ class Values(Sqlite3DB): def get_max_length(self, path): - if DEBUG: + if DEBUG: # pragma: no cover print('get_max_length', path) val_max = self._storage.select("SELECT max(idx) FROM value WHERE path = ? AND session_id = ?", (path, self._session_id), False) diff --git a/tiramisu/value.py b/tiramisu/value.py index fbad7a1..e30137e 100644 --- a/tiramisu/value.py +++ b/tiramisu/value.py @@ -31,8 +31,7 @@ class Values(object): '__weakref__') def __init__(self, - storage, - default_owner=owners.user): + storage): """ Initializes the values's dict. @@ -42,11 +41,12 @@ class Values(object): # store the storage self._p_ = storage # set default owner - self._p_.setvalue(None, - None, - default_owner, - None, - True) + if self._p_.getowner(None, None) is None: + self._p_.setvalue(None, + None, + owners.user, + None, + True) #______________________________________________________________________ # get value