1 # -*- coding: utf-8 -*-
2 "takes care of the option's values and multi values"
3 # Copyright (C) 2013-2017 Team tiramisu (see AUTHORS for all contributors)
5 # This program is free software: you can redistribute it and/or modify it
6 # under the terms of the GNU Lesser General Public License as published by the
7 # Free Software Foundation, either version 3 of the License, or (at your
8 # option) any later version.
10 # This program is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15 # You should have received a copy of the GNU Lesser General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 # ____________________________________________________________
21 from .error import ConfigError, SlaveError, PropertiesOptionError
22 from .setting import owners, expires_time, undefined
23 from .autolib import carry_out_calculation
25 from .option import SymLinkOption, DynSymLinkOption, Option
30 """The `Config`'s root is indeed in charge of the `Option()`'s values,
31 but the values are physicaly located here, in `Values`, wich is also
32 responsible of a caching utility.
34 __slots__ = ('context', '_p_', '__weakref__')
36 def __init__(self, context, storage):
38 Initializes the values's dict.
40 :param context: the context is the home config's values
43 self.context = weakref.ref(context)
44 # the storage type is dictionary or sqlite3
47 def _getcontext(self):
48 """context could be None, we need to test it
49 context is None only if all reference to `Config` object is deleted
50 (for example we delete a `Config` and we manipulate a reference to
51 old `SubConfig`, `Values`, `Multi` or `Settings`)
53 context = self.context()
55 raise ConfigError(_('the context does not exist anymore'))
58 def _get_multi(self, opt, path):
59 return Multi([], self.context, opt, path)
61 def _getdefaultvalue(self, opt, path, with_meta, index, submulti_index, validate):
62 # if value has callback and is not set
63 if opt.impl_has_callback():
64 callback, callback_params = opt.impl_get_callback()
65 value = carry_out_calculation(opt, context=self._getcontext(),
67 callback_params=callback_params,
68 index=index, validate=validate)
69 if isinstance(value, list) and index is not None:
70 #if return a list and index is set, return value only if
71 #it's a submulti without submulti_index and without list of list
72 if opt.impl_is_submulti() and submulti_index is undefined and \
73 (len(value) == 0 or not isinstance(value[0], list)):
75 if not opt.impl_is_submulti() and len(value) > index:
80 meta = self._getcontext().cfgimpl_get_meta()
82 value = meta.cfgimpl_get_values(
83 )._get_cached_value(opt, path, index=index, submulti_index=submulti_index,
84 from_masterslave=True)
85 if isinstance(value, Exception):
86 if not isinstance(value, PropertiesOptionError): # pragma: no cover
89 if isinstance(value, Multi):
92 if isinstance(value, SubMulti):
93 if submulti_index is not undefined:
94 value = value[submulti_index]
100 if isinstance(val, SubMulti):
102 new_value.append(val)
106 # now try to get default value
107 value = opt.impl_getdefault()
108 if opt.impl_is_multi() and index is not None:
110 value = opt.impl_getdefault_multi()
112 if len(value) > index:
115 value = opt.impl_getdefault_multi()
118 def _getvalue(self, opt, path, self_properties, index, submulti_index,
119 with_meta, masterlen, session, validate):
120 """actually retrieves the value
122 :param opt: the `option.Option()` object
123 :returns: the option's value (or the default value if not set)
125 force_default = 'frozen' in self_properties and \
126 'force_default_on_freeze' in self_properties
128 if index is None or not opt.impl_is_master_slaves('slave'):
132 is_default = self._p_.getowner(path, owners.default, session, only_default=True, index=_index) == owners.default
133 if not is_default and not force_default:
134 if opt.impl_is_master_slaves('slave'):
135 return self._p_.getvalue(path, session, index)
137 value = self._p_.getvalue(path, session)
138 if index is not None:
139 if len(value) > index:
141 #value is smaller than expected
142 #so return default value
145 return self._getdefaultvalue(opt, path, with_meta, index,
146 submulti_index, validate)
148 def get_modified_values(self):
149 return self._p_.get_modified_values()
151 def __contains__(self, opt):
153 implements the 'in' keyword syntax in order provide a pythonic way
154 to kow if an option have a value
156 :param opt: the `option.Option()` object
158 path = opt.impl_getpath(self._getcontext())
159 return self._contains(path)
161 def _contains(self, path, session=None):
163 session = self._p_.getsession()
164 return self._p_.hasvalue(path, session)
166 def __delitem__(self, opt):
167 """overrides the builtins `del()` instructions"""
170 def reset(self, opt, path=None, validate=True, _setting_properties=None):
171 context = self._getcontext()
172 setting = context.cfgimpl_get_settings()
174 path = opt.impl_getpath(context)
175 if _setting_properties is None:
176 _setting_properties = setting._getproperties(read_write=False)
177 session = self._p_.getsession()
178 hasvalue = self._contains(path, session)
180 if validate and hasvalue and 'validator' in _setting_properties:
181 session = context.cfgimpl_get_values()._p_.getsession()
182 fake_context = context._gen_fake_values(session)
183 fake_value = fake_context.cfgimpl_get_values()
184 fake_value.reset(opt, path, validate=False)
185 ret = fake_value._get_cached_value(opt, path,
186 setting_properties=_setting_properties,
188 if isinstance(ret, Exception):
190 if opt.impl_is_master_slaves('master'):
191 opt.impl_get_master_slaves().reset(opt, self, _setting_properties)
193 if 'force_store_value' in setting._getproperties(opt=opt,
195 setting_properties=_setting_properties,
197 apply_requires=False):
198 value = self._getdefaultvalue(opt, path, True, undefined, undefined, validate)
199 if isinstance(value, Exception): # pragma: no cover
201 self._setvalue(opt, path, value, force_owner=owners.forced)
203 self._p_.resetvalue(path, session)
204 context.cfgimpl_reset_cache(opt=opt, path=path)
206 def _isempty(self, opt, value, force_allow_empty_list=False, index=None):
207 "convenience method to know if an option is empty"
208 if value is undefined:
212 if index in [None, undefined] and opt.impl_is_multi():
213 if force_allow_empty_list:
214 allow_empty_list = True
216 allow_empty_list = opt.impl_allow_empty_list()
217 if allow_empty_list is undefined:
218 if opt.impl_is_master_slaves('slave'):
219 allow_empty_list = True
221 allow_empty_list = False
222 isempty = value is None or (not allow_empty_list and value == []) or \
223 None in value or empty in value
225 isempty = value is None or value == empty
228 def __getitem__(self, opt):
229 "enables us to use the pythonic dictionary-like access to values"
230 return self._get_cached_value(opt)
232 def getitem(self, opt, validate=True, force_permissive=False):
235 return self._get_cached_value(opt, validate=validate,
236 force_permissive=force_permissive)
238 def _get_cached_value(self, opt, path=None, validate=True,
239 force_permissive=False, trusted_cached_properties=True,
240 validate_properties=True,
241 setting_properties=undefined, self_properties=undefined,
242 index=None, submulti_index=undefined, from_masterslave=False,
243 with_meta=True, masterlen=undefined, check_frozen=False,
244 session=None, display_warnings=True):
245 context = self._getcontext()
246 settings = context.cfgimpl_get_settings()
248 path = opt.impl_getpath(context)
250 if setting_properties is undefined:
251 setting_properties = settings._getproperties(read_write=False)
252 if self_properties is undefined:
253 self_properties = settings._getproperties(opt, path,
255 setting_properties=setting_properties,
257 if 'cache' in setting_properties and self._p_.hascache(path, index):
258 if 'expire' in setting_properties:
260 is_cached, value = self._p_.getcache(path, ntime, index)
262 if opt.impl_is_multi() and not isinstance(value, Multi) and index is None:
263 value = Multi(value, self.context, opt, path)
264 if not trusted_cached_properties:
265 # revalidate properties (because of not default properties)
266 props = settings.validate_properties(opt, False, False, value=value,
268 force_permissive=force_permissive,
269 setting_properties=setting_properties,
270 self_properties=self_properties,
276 session = self._p_.getsession()
277 if not from_masterslave and opt.impl_is_master_slaves():
278 val = opt.impl_get_master_slaves().getitem(self, opt, path,
281 trusted_cached_properties,
284 setting_properties=setting_properties,
286 self_properties=self_properties,
287 check_frozen=check_frozen)
289 val = self._get_validated_value(opt, path, validate,
297 submulti_index=submulti_index,
298 check_frozen=check_frozen,
300 display_warnings=display_warnings)
301 if isinstance(val, Exception):
303 # cache doesn't work with SubMulti yet
304 if not isinstance(val, SubMulti) and 'cache' in setting_properties and \
305 validate and validate_properties and force_permissive is False \
306 and trusted_cached_properties is True:
307 if 'expire' in setting_properties:
310 ntime = ntime + expires_time
311 self._p_.setcache(path, val, ntime, index)
314 def _get_validated_value(self, opt, path, validate, force_permissive,
315 validate_properties, setting_properties,
317 index=None, submulti_index=undefined,
321 session=None, display_warnings=True):
322 """same has getitem but don't touch the cache
323 index is None for slave value, if value returned is not a list, just return []
325 context = self._getcontext()
326 setting = context.cfgimpl_get_settings()
329 session = self._p_.getsession()
330 value = self._getvalue(opt, path, self_properties, index, submulti_index,
331 with_meta, masterlen, session, validate)
332 if isinstance(value, Exception):
334 if isinstance(value, ConfigError):
335 # For calculating properties, we need value (ie for mandatory
337 # If value is calculating with a PropertiesOptionError's option
338 # _getvalue raise a ConfigError.
339 # We can not raise ConfigError if this option should raise
340 # PropertiesOptionError too. So we get config_error and raise
341 # ConfigError if properties did not raise.
343 # value is not set, for 'undefined' (cannot set None because of
344 # mandatory property)
346 else: # pragma: no cover
350 if opt.impl_is_multi():
352 value = Multi(value, self.context, opt, path)
353 elif opt.impl_is_submulti() and submulti_index is undefined:
354 value = SubMulti(value, self.context, opt, path,
358 if submulti_index is undefined:
359 force_submulti_index = None
361 force_submulti_index = submulti_index
362 err = opt.impl_validate(value, context,
363 'validator' in setting_properties,
365 force_submulti_index=force_submulti_index,
367 display_warnings=False)
372 if validate_properties:
373 if config_error is not None:
374 # should not raise PropertiesOptionError if option is
376 val_props = undefined
379 props = setting.validate_properties(opt, False, check_frozen, value=val_props,
381 force_permissive=force_permissive,
382 setting_properties=setting_properties,
383 self_properties=self_properties,
387 if not value_error and validate and display_warnings:
388 opt.impl_validate(value, context,
389 'validator' in setting_properties,
391 force_submulti_index=force_submulti_index,
393 display_warnings=display_warnings,
394 setting_properties=setting_properties,)
395 if config_error is not None:
399 def __setitem__(self, opt, value):
400 raise ConfigError(_('you should only set value with config'))
402 def setitem(self, opt, value, path, force_permissive=False,
403 check_frozen=True, not_raises=False, index=None):
404 # check_frozen is, for example, used with "force_store_value"
405 # user didn't change value, so not write
407 context = self._getcontext()
408 setting_properties = context.cfgimpl_get_settings()._getproperties(read_write=False)
409 if 'validator' in setting_properties:
410 session = context.cfgimpl_get_values()._p_.getsession()
411 fake_context = context._gen_fake_values(session)
412 fake_values = fake_context.cfgimpl_get_values()
413 fake_values._setvalue(opt, path, value, index=index)
414 props = fake_values.validate(opt, value, path,
415 check_frozen=check_frozen,
416 force_permissive=force_permissive,
417 setting_properties=setting_properties,
418 session=session, not_raises=not_raises,
420 if props and not_raises:
422 err = opt.impl_validate(value, fake_context, display_warnings=False, force_index=index)
425 opt.impl_validate(value, fake_context, display_error=False)
426 self._setvalue(opt, path, value, index=index)
428 def _setvalue(self, opt, path, value, force_owner=undefined, index=None):
429 context = self._getcontext()
430 context.cfgimpl_reset_cache(opt=opt, path=path)
431 if force_owner is undefined:
432 owner = context.cfgimpl_get_settings().getowner()
435 # in storage, value must not be a multi
436 if isinstance(value, Multi):
437 if not opt.impl_is_master_slaves('slave') or index is None:
439 if opt.impl_is_submulti():
440 for idx, val in enumerate(value):
441 if isinstance(val, SubMulti):
442 value[idx] = list(val)
445 session = self._p_.getsession()
446 #FIXME pourquoi là et pas dans masterslaves ??
447 if opt.impl_is_master_slaves('slave'):
448 if index is not None:
449 self._p_.setvalue(path, value, owner, index, session)
451 self._p_.resetvalue(path, session)
452 for idx, val in enumerate(value):
453 self._p_.setvalue(path, val, owner, idx, session)
455 self._p_.setvalue(path, value, owner, None, session)
458 def validate(self, opt, value, path, check_frozen=True, force_permissive=False,
459 setting_properties=undefined, valid_masterslave=True,
460 not_raises=False, session=None, index=None):
461 if valid_masterslave and opt.impl_is_master_slaves():
463 session = self._p_.getsession()
464 if index is not None:
468 len_value = len(value)
470 val = opt.impl_get_master_slaves().validate(self, opt, len_value, path, session, setitem=setitem)
471 if isinstance(val, Exception):
473 props = self._getcontext().cfgimpl_get_settings().validate_properties(opt,
478 force_permissive=force_permissive,
479 setting_properties=setting_properties,
486 def _is_meta(self, opt, path, session):
487 context = self._getcontext()
488 setting = context.cfgimpl_get_settings()
489 self_properties = setting._getproperties(opt, path, read_write=False)
490 if 'frozen' in self_properties and 'force_default_on_freeze' in self_properties:
492 if self._p_.getowner(path, owners.default, session, only_default=True) is not owners.default:
494 if context.cfgimpl_get_meta() is not None:
498 def getowner(self, opt, index=None, force_permissive=False, session=None):
500 retrieves the option's owner
502 :param opt: the `option.Option` object
503 :param force_permissive: behaves as if the permissive property
505 :returns: a `setting.owners.Owner` object
507 if isinstance(opt, SymLinkOption) and \
508 not isinstance(opt, DynSymLinkOption):
509 opt = opt._impl_getopt()
510 path = opt.impl_getpath(self._getcontext())
511 return self._getowner(opt, path, session, index=index, force_permissive=force_permissive)
513 def _getowner(self, opt, path, session, validate_properties=True,
514 force_permissive=False, validate_meta=undefined,
515 self_properties=undefined, only_default=False,
517 """get owner of an option
520 session = self._p_.getsession()
521 if not isinstance(opt, Option) and not isinstance(opt,
523 raise ConfigError(_('owner only avalaible for an option'))
524 context = self._getcontext()
525 if self_properties is undefined:
526 self_properties = context.cfgimpl_get_settings()._getproperties(
527 opt, path, read_write=False)
528 if 'frozen' in self_properties and 'force_default_on_freeze' in self_properties:
529 return owners.default
530 if validate_properties:
531 value = self._get_cached_value(opt, path, True, force_permissive, None, True,
532 self_properties=self_properties, session=session,
534 if isinstance(value, Exception):
537 owner = self._p_.getowner(path, owners.default, session, only_default=only_default, index=index)
538 if validate_meta is undefined:
539 if opt.impl_is_master_slaves('slave'):
540 master = opt.impl_get_master_slaves().getmaster(opt)
541 masterp = master.impl_getpath(context)
542 validate_meta = self._is_meta(opt, masterp, session)
546 meta = context.cfgimpl_get_meta()
547 if owner is owners.default and meta is not None:
548 owner = meta.cfgimpl_get_values()._getowner(opt, path, session,
549 validate_properties=validate_properties,
550 force_permissive=force_permissive,
551 self_properties=self_properties,
552 only_default=only_default, index=index)
555 def setowner(self, opt, owner, index=None):
557 sets a owner to an option
559 :param opt: the `option.Option` object
560 :param owner: a valid owner, that is a `setting.owners.Owner` object
562 if not isinstance(owner, owners.Owner):
563 raise TypeError(_("invalid generic owner {0}").format(str(owner)))
565 path = opt.impl_getpath(self._getcontext())
566 session = self._p_.getsession()
567 if not self._p_.hasvalue(path, session):
568 raise ConfigError(_('no value for {0} cannot change owner to {1}'
569 '').format(path, owner))
570 props = self._getcontext().cfgimpl_get_settings().validate_properties(opt,
577 self._p_.setowner(path, owner, session, index=index)
579 def is_default_owner(self, opt, validate_properties=True,
580 validate_meta=True, index=None,
581 force_permissive=False):
583 :param config: *must* be only the **parent** config
584 (not the toplevel config)
587 path = opt.impl_getpath(self._getcontext())
588 return self._is_default_owner(opt, path, session=None,
589 validate_properties=validate_properties,
590 validate_meta=validate_meta, index=index,
591 force_permissive=force_permissive)
593 def _is_default_owner(self, opt, path, session, validate_properties=True,
594 validate_meta=True, self_properties=undefined,
595 index=None, force_permissive=False):
596 d = self._getowner(opt, path, session, validate_properties=validate_properties,
597 validate_meta=validate_meta,
598 self_properties=self_properties, only_default=True,
599 index=index, force_permissive=force_permissive)
600 return d == owners.default
603 def set_information(self, key, value):
604 """updates the information's attribute
606 :param key: information's key (ex: "help", "doc"
607 :param value: information's value (ex: "the help string")
609 self._p_.set_information(key, value)
611 def get_information(self, key, default=undefined):
612 """retrieves one information's item
614 :param key: the item string (ex: "help")
616 return self._p_.get_information(key, default)
618 def del_information(self, key, raises=True):
619 self._p_.del_information(key, raises)
621 def mandatory_warnings(self, force_permissive=True):
622 """convenience function to trace Options that are mandatory and
623 where no value has been set
625 :returns: generator of mandatory Option's path
627 context = self._getcontext()
628 settings = context.cfgimpl_get_settings()
629 setting_properties = context.cfgimpl_get_settings()._getproperties()
630 setting_properties.update(['mandatory', 'empty'])
631 def _is_properties_option(err, path):
632 if not isinstance(err, Exception):
634 elif isinstance(err, PropertiesOptionError):
635 if err.proptype == ['mandatory']:
637 elif isinstance(err, ConfigError):
638 #assume that uncalculated value is an empty value
643 def _mandatory_warnings(description, currpath=None):
646 for opt in description._impl_getchildren(context=context):
647 name = opt.impl_getname()
648 path = '.'.join(currpath + [name])
650 if opt.impl_is_optiondescription():
651 if not settings.validate_properties(opt, True, False, path=path,
652 force_permissive=True,
653 setting_properties=setting_properties):
654 for path in _mandatory_warnings(opt, currpath + [name]):
657 if isinstance(opt, SymLinkOption) and \
658 not isinstance(opt, DynSymLinkOption):
660 self_properties = settings._getproperties(opt, path,
662 setting_properties=setting_properties)
663 if 'mandatory' in self_properties or 'empty' in self_properties:
664 err = self._get_cached_value(opt, path=path,
665 trusted_cached_properties=False,
666 force_permissive=True,
667 setting_properties=setting_properties,
668 self_properties=self_properties,
670 display_warnings=False)
671 if opt.impl_is_master_slaves('slave') and isinstance(err, list):
673 ret = _is_properties_option(val, path)
678 ret = _is_properties_option(err, path)
682 descr = context.cfgimpl_get_description()
683 for path in _mandatory_warnings(descr):
686 def force_cache(self):
687 """parse all option to force data in cache
689 context = self.context()
690 if not 'cache' in context.cfgimpl_get_settings():
691 raise ConfigError(_('can force cache only if cache '
692 'is actived in config'))
693 #FIXME properties and value should update "expired" time
694 for path in context.cfgimpl_get_description().impl_getpaths(
695 include_groups=True):
696 err = context.getattr(path, returns_raise=True)
697 if isinstance(err, Exception) and not isinstance(err, PropertiesOptionError): # pragma: no cover
700 def __getstate__(self):
701 return {'_p_': self._p_}
703 def _impl_setstate(self, storage):
704 self._p_._storage = storage
706 def __setstate__(self, states):
707 self._p_ = states['_p_']
710 # ____________________________________________________________
713 """multi options values container
714 that support item notation for the values of multi options"""
715 __slots__ = ('opt', 'path', 'context', '__weakref__')
717 def __init__(self, value, context, opt, path):
719 :param value: the Multi wraps a list value
720 :param context: the home config that has the values
721 :param opt: the option object that have this Multi value
722 :param path: path of the option
726 if not opt.impl_is_submulti() and isinstance(value, Multi):
727 raise ValueError(_('{0} is already a Multi ').format(
731 if not isinstance(context, weakref.ReferenceType):
732 raise ValueError('context must be a Weakref')
733 self.context = context
734 if not isinstance(value, list):
735 if not '_index' in self.__slots__ and opt.impl_is_submulti():
739 elif value != [] and not '_index' in self.__slots__ and \
740 opt.impl_is_submulti() and not isinstance(value[0], list):
742 super(Multi, self).__init__(value)
743 if opt.impl_is_submulti():
744 if not '_index' in self.__slots__:
745 for idx, val in enumerate(self):
746 if not isinstance(val, SubMulti):
747 super(Multi, self).__setitem__(idx, SubMulti(val,
751 self[idx].submulti = weakref.ref(self)
753 def _getcontext(self):
754 """context could be None, we need to test it
755 context is None only if all reference to `Config` object is deleted
756 (for example we delete a `Config` and we manipulate a reference to
757 old `SubConfig`, `Values`, `Multi` or `Settings`)
759 context = self.context()
761 raise ConfigError(_('the context does not exist anymore'))
764 def __setitem__(self, index, value):
765 self._setitem(index, value)
767 def _setitem(self, index, value, validate=True):
768 context = self._getcontext()
769 setting = context.cfgimpl_get_settings()
770 setting_properties = setting._getproperties(read_write=False)
772 index = self.__len__() + index
773 if 'validator' in setting_properties and validate:
774 session = context.cfgimpl_get_values()._p_.getsession()
775 fake_context = context._gen_fake_values(session)
776 fake_multi = Multi(list(self), weakref.ref(fake_context), self.opt, self.path)
777 fake_multi._setitem(index, value, validate=False)
778 self._validate(value, fake_context, index, True)
779 #assume not checking mandatory property
780 super(Multi, self).__setitem__(index, value)
781 self._store(index=index)
783 #def __repr__(self, *args, **kwargs):
784 # return super(Multi, self).__repr__(*args, **kwargs)
786 def __getitem__(self, index):
787 value = super(Multi, self).__getitem__(index)
788 if isinstance(value, PropertiesOptionError):
792 def __delitem__(self, index):
793 return self.pop(index)
795 def _getdefaultvalue(self, index):
796 values = self._getcontext().cfgimpl_get_values()
797 value = values._getdefaultvalue(self.opt, self.path, True, index,
799 if self.opt.impl_is_submulti():
800 value = SubMulti(value, self.context, self.opt, self.path, index)
803 def append(self, value=undefined, force=False, setitem=True, validate=True,
804 force_permissive=False):
805 """the list value can be updated (appened)
806 only if the option is a master
808 if not force and self.opt.impl_is_master_slaves('slave'):
809 raise SlaveError(_("cannot append a value on a multi option {0}"
810 " which is a slave").format(self.opt.impl_getname()))
811 index = self.__len__()
812 if value is undefined:
813 value = self._getdefaultvalue(index)
814 if validate and value not in [None, undefined]:
815 context = self._getcontext()
816 setting = context.cfgimpl_get_settings()
817 setting_properties = setting._getproperties(read_write=False)
818 if 'validator' in setting_properties:
819 session = context.cfgimpl_get_values()._p_.getsession()
820 fake_context = context._gen_fake_values(session)
821 fake_multi = Multi(list(self), weakref.ref(fake_context), self.opt, self.path)
822 if isinstance(fake_multi, Exception):
824 fake_multi.append(value, validate=False, force=True,
826 self._validate(value, fake_context, index, True)
827 if not '_index' in self.__slots__ and self.opt.impl_is_submulti():
828 if not isinstance(value, SubMulti):
829 value = SubMulti(value, self.context, self.opt, self.path, index)
830 value.submulti = weakref.ref(self)
831 super(Multi, self).append(value)
833 self._store(force=force)
835 def append_properties_error(self, err):
836 super(Multi, self).append(err)
838 def sort(self, cmp=None, key=None, reverse=False):
839 if self.opt.impl_is_master_slaves():
840 raise SlaveError(_("cannot sort multi option {0} if master or slave"
841 "").format(self.opt.impl_getname()))
842 if sys.version_info[0] >= 3: # pragma: no cover
844 raise ValueError(_('cmp is not permitted in python v3 or '
846 super(Multi, self).sort(key=key, reverse=reverse)
848 super(Multi, self).sort(cmp=cmp, key=key, reverse=reverse)
852 if self.opt.impl_is_master_slaves():
853 raise SlaveError(_("cannot reverse multi option {0} if master or "
854 "slave").format(self.opt.impl_getname()))
855 super(Multi, self).reverse()
858 def insert(self, index, value, validate=True):
859 if self.opt.impl_is_master_slaves():
860 raise SlaveError(_("cannot insert multi option {0} if master or "
861 "slave").format(self.opt.impl_getname()))
862 context = self._getcontext()
863 setting = setting = context.cfgimpl_get_settings()
864 setting_properties = setting._getproperties(read_write=False)
865 if 'validator' in setting_properties and validate and value is not None:
866 session = context.cfgimpl_get_values()._p_.getsession()
867 fake_context = context._gen_fake_values(session)
868 fake_multi = Multi(list(self), weakref.ref(fake_context), self.opt, self.path)
869 fake_multi.insert(index, value, validate=False)
870 self._validate(value, fake_context, index, True)
871 super(Multi, self).insert(index, value)
874 def extend(self, iterable, validate=True):
875 if self.opt.impl_is_master_slaves():
876 raise SlaveError(_("cannot extend multi option {0} if master or "
877 "slave").format(self.opt.impl_getname()))
878 index = getattr(self, '_index', None)
879 context = self._getcontext()
880 setting = context.cfgimpl_get_settings()
881 setting_properties = setting._getproperties(read_write=False)
882 if 'validator' in setting_properties and validate:
883 session = context.cfgimpl_get_values()._p_.getsession()
884 fake_context = context._gen_fake_values(session)
885 fake_multi = Multi(list(self), weakref.ref(fake_context), self.opt, self.path)
887 fake_multi.extend(iterable, validate=False)
888 self._validate(fake_multi, fake_context, index)
890 fake_multi[index].extend(iterable, validate=False)
891 self._validate(fake_multi[index], fake_context, index)
892 super(Multi, self).extend(iterable)
895 def _validate(self, value, fake_context, force_index, submulti=False):
896 err = self.opt.impl_validate(value, context=fake_context,
897 force_index=force_index,
902 def pop(self, index, force=False):
903 """the list value can be updated (poped)
904 only if the option is a master
906 :param index: remove item a index
908 :param force: force pop item (withoud check master/slave)
910 :returns: item at index
912 context = self._getcontext()
914 if self.opt.impl_is_master_slaves('slave'):
915 raise SlaveError(_("cannot pop a value on a multi option {0}"
916 " which is a slave").format(self.opt.impl_getname()))
917 if self.opt.impl_is_master_slaves('master'):
918 self.opt.impl_get_master_slaves().pop(self.opt,
919 context.cfgimpl_get_values(), index)
920 #set value without valid properties
921 ret = super(Multi, self).pop(index)
922 self._store(force=force)
925 def remove(self, value):
926 idx = self.index(value)
929 def _store(self, force=False, index=None):
930 values = self._getcontext().cfgimpl_get_values()
932 #FIXME could get properties an pass it
933 values.validate(self.opt, self, self.path,
934 valid_masterslave=False)
935 values._setvalue(self.opt, self.path, self, index=index)
938 class SubMulti(Multi):
939 __slots__ = ('_index', 'submulti')
941 def __init__(self, value, context, opt, path, index):
943 :param index: index (only for slave with submulti)
947 super(SubMulti, self).__init__(value, context, opt, path)
949 def append(self, value=undefined):
950 super(SubMulti, self).append(value, force=True)
952 def pop(self, index):
953 return super(SubMulti, self).pop(index, force=True)
955 def __setitem__(self, index, value):
956 self._setitem(index, value)
958 def _store(self, force=False, index=None):
959 #force is unused here
960 values = self._getcontext().cfgimpl_get_values()
961 values.validate(self.opt, self, self.path, valid_masterslave=False)
962 values._setvalue(self.opt, self.path, self.submulti())
964 def _validate(self, value, fake_context, force_index, submulti=False):
965 if value is not None:
966 if submulti is False:
967 super(SubMulti, self)._validate(value, fake_context,
968 force_index, submulti)
970 err = self.opt.impl_validate(value, context=fake_context,
971 force_index=self._index,
972 force_submulti_index=force_index,
977 def _getdefaultvalue(self, index):
978 values = self._getcontext().cfgimpl_get_values()
979 return values._getdefaultvalue(self.opt, self.path, True, index,